2014-01-19 3 views
0

Поплавковые умножения, приводящие к результатам, ограниченным швом FLT_MIN, очень медленны по сравнению с другими поплавковыми умножениями. Выполнение кода примера ниже на моей машине Linux, я получил следующие результаты:Огромное истекшее время для умножения FLT_MIN

Elapsed time for 1E09 iterations of 0 * 0.900000 : 2.623269 s 
Elapsed time for 1E09 iterations of 1.17549e-38 * 0.900000 : 73.851011 s 
Elapsed time for 1E09 iterations of 2.35099e-38 * 0.900000 : 2.637788 s 
Elapsed time for 1E09 iterations of 0.00870937 * 0.900000 : 2.632788 s 
Elapsed time for 1E09 iterations of 1 * 0.900000 : 2.654571 s 
Elapsed time for 1E09 iterations of 3.40282e+38 * 0.900000 : 2.639316 s 

Операция 1.17549e-38 * 0,9, кажется, принять по крайней мере, в 25 раз больше, чем в других тестируемых операций умножения. Это хорошо известная проблема?

В случае критического проекта, в котором необходимо выполнить большое количество таких умножений, потенциально приводящих к FLT_MIN, что может быть быстрым способом обойти эту проблему? (Я не могу позволить, чтобы проверить каждое значение перед умножением, но я мог допустить ошибку порядка й-5 в результате умножения)

#include <sys/time.h> 
#include <stdio.h> 
#include <float.h> 
#define N_VALS 6 
#define ALMOST_MIN FLT_MIN*2 
int timeval_subtract (struct timeval *result,struct timeval * start,struct timeval *stop) 
{ 
    long int sdiff= stop-> tv_sec - start->tv_sec; 
    long int udiff=stop->tv_usec - start-> tv_usec; 
    if (udiff<0) 
    { 
    udiff=1000000+udiff; 
    sdiff--; 
    } 
    result->tv_sec = sdiff; 
    result->tv_usec = udiff; 
} 

int main() 
{ 
    float values [N_VALS]={0.0f,FLT_MIN,ALMOST_MIN, 0.00870937f, 1.0f, FLT_MAX}; 
    float out, mul=0.9f; 
    int i, j, err; 
    struct timeval t_start, t_stop, t_elaps; 
    for (j=0; j<N_VALS; j++) 
    { 
    err=gettimeofday(&t_start, NULL); 
    for (i=0; i<1000000000; i++) 
     out=values[j]*mul; 

    err=gettimeofday(&t_stop, NULL); 
    timeval_subtract(&t_elaps, &t_start, &t_stop); 
    printf("Elapsed time for 1E09 iterations of %g * %f : %ld.%06ld s \n", values[j], mul, t_elaps.tv_sec, t_elaps.tv_usec); 
    } 
} 
+0

Возможно, вы столкнулись с [sub-normal] (http://en.wikipedia.org/wiki/Denormal_number) цифрами, которые занимают больше времени с помощью аппаратного или программного обеспечения FP - хорошо известной проблемы. – chux

+0

Мне было бы интересно узнать производительность, если бы вы использовали 'double', но ограничили ваш диапазон' float'. Какой может быть случай? – chux

+0

какой компилятор вы используете? –

ответ

1

Причины это занимает гораздо больше времени, чтобы сделать .9 * FLT_MIN заключается в том, что результат меньше наименьшего значения, которое может представлять float. Это приводит к тому, что процессор вызывает исключение, которое обрабатывается ОС и может включать вызовы в пользовательском пространстве. Это занимает много времени, по сравнению с простым умножением с плавающей запятой, которое полностью выполняется в аппаратном обеспечении.

Как это исправить? Зависит от вашей платформы и инструментов сборки. Если вы используете gcc, то он пытается использовать настройки CPU для оптимизации некоторых операций, в зависимости от того, какие флаги вы устанавливаете. Посмотрите руководство gcc для -ffast-math и соответствующих флагов оптимизации плавающей запятой. Обратите внимание, что использование этих флагов может привести к тому, что результаты не будут соответствовать спецификации IEEE с плавающей запятой.

+0

Вы не должны видеть большого штрафа за производительность для суб-нормалей на Sandy Bridge или более новом процессоре. -ffast-math устанавливает все сгенерированные под нормали к нулю. – tim18