Авто векторизация по двойному и быстрому математике
Почему это обязательно использовать -ffast-math
с g++ для достижения векторизации циклов, используя double
s? Мне не нравится -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
, потому что математика с плавающей точкой не является ни коммутативной, ни ассоциативной.
- Является ли сложение с плавающей точкой коммутативным и ассоциативным?
- Является ли сложение с плавающей точкой коммутативным в C++?
- Являются ли операции с плавающей точкой в C ассоциативными?
- Является ли сложение с плавающей точкой и умножение ассоциативными?
По этой же причине компиляторы не могут оптимизировать 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 .
- -fassociative-math можно использовать вместо -ffast-math, чтобы включить векторизацию редукции чисел с плавающей запятой (04 сентября 2007 г.).