Авто векторизация по двойному и быстрому математике

Почему это обязательно использовать -ffast-math с g++ для достижения векторизации циклов, используя doubles? Мне не нравится -ffast-math потому что я не хочу терять точность.

4 ответа

Решение

Вы не обязательно теряете точность с -ffast-math, Это влияет только на обработку NaN, Inf и т. д. и порядок, в котором выполняются операции.

Если у вас есть определенный фрагмент кода, в котором вы не хотите, чтобы GCC переупорядочивал или упрощал вычисления, вы можете пометить переменные как используемые с помощью asm заявление.

Например, следующий код выполняет операцию округления f, Тем не менее, два f += g а также f -= g операции могут быть оптимизированы с помощью gcc:

static double moo(double f, double g)                                      
{                                                                          
    g *= 4503599627370496.0; // 2 ** 52                                    
    f += g;                                                                
    f -= g;                                                                
    return f;                                                            
}                                                                     

На x86_64 вы можете использовать это asm Заявление для инструктирования GCC не выполнять эту оптимизацию:

static double moo(double f, double g)                                      
{                                                                          
    g *= 4503599627370496.0; // 2 ** 52                                    
    f += g;                                                                
    __asm__("" : "+x" (f));
    f -= g;
    return f;
}

К сожалению, вам нужно будет адаптировать это для каждой архитектуры. На PowerPC используйте +f вместо +x,

Так как -ffast-math позволяет переупорядочивать операнды, что позволяет векторизовать много кода.

Например, чтобы рассчитать это

sum = a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + … a[99]

компилятор обязан делать дополнения последовательно без -ffast-math, потому что математика с плавающей точкой не является ни коммутативной, ни ассоциативной.

По этой же причине компиляторы не могут оптимизировать a*a*a*a*a*a в (a*a*a)*(a*a*a) без -ffast-math

Это означает, что векторизация недоступна, если у вас нет очень эффективных горизонтальных векторных добавлений.

Однако если -ffast-math включено, выражение может быть вычислено следующим образом (посмотрите на A7. Auto-Vectorization)

sum0 = a[0] + a[4] + a[ 8] + … a[96]
sum1 = a[1] + a[5] + a[ 9] + … a[97]
sum2 = a[2] + a[6] + a[10] + … a[98]
sum3 = a[3] + a[7] + a[11] + … a[99]
sum’ = sum0 + sum1 + sum2 + sum3

Теперь компилятор может легко векторизовать его, добавив каждый столбец параллельно, а затем выполнить горизонтальное добавление в конце

Есть ли sum’ == sum? Только если (a[0]+a[4]+…) + (a[1]+a[5]+…) + (a[2]+a[6]+…) + ([a[3]+a[7]+…) == a[0] + a[1] + a[2] + … Это имеет место при ассоциативности, которой поплавки не придерживаются, все время. Определение /fp:fast позволяет компилятору преобразовать ваш код, чтобы он работал быстрее - до 4 раз быстрее, для этого простого вычисления.

Вы предпочитаете быстрый или точный? - А7. Авто-Vectorization

Это может быть включено -fassociative-math флаг в gcc

Дальнейшие чтения

Очень вероятно, потому что векторизация означает, что у вас могут быть разные результаты, или вы можете пропустить сигналы / исключения с плавающей запятой.

Если вы компилируете для 32-битной x86, тогда gcc и g++ по умолчанию используют x87 для математики с плавающей запятой, на 64-битной - по умолчанию sse, однако x87 может и будет выдавать разные значения для одного и того же вычисления, поэтому маловероятно, что g++ рассмотрит векторизация, если она не может гарантировать, что вы получите те же результаты, если вы не используете -ffast-math или некоторые из включенных флагов.

По сути, все сводится к тому, что среда с плавающей запятой для векторизованного кода может отличаться от среды для векторизованного кода, иногда важными способами, если различия не имеют значения для вас, что-то вроде

-fno-math-errno -fno-trapping-math -fno-signaling-nans -fno-rounding-math

но сначала посмотрите эти параметры и убедитесь, что они не повлияют на правильность вашей программы. -ffinite-math-only может помочь также

Чтобы включить автоматическую векторизацию с помощью gcc, ffast-math на самом деле не нужен. См. https://gcc.gnu.org/projects/tree-ssa/vectorization.html#using

Чтобы включить векторизацию сокращений с плавающей запятой, используйте -ffast-math или -fassociative-math.

Использование -fassociative-math должно быть достаточным.

Так было с 2007 года, см. https://gcc.gnu.org/projects/tree-ssa/vectorization.html#oldnews .

  1. -fassociative-math можно использовать вместо -ffast-math, чтобы включить векторизацию редукции чисел с плавающей запятой (04 сентября 2007 г.).
Другие вопросы по тегам