Автоматическая векторизация в double и ffast-math
-
27-09-2019 - |
Вопрос
Почему это обязательно для использования -ffast-math
с помощью g ++ для достижения векторизации циклов используется double
с?Мне не нравится -ffast-math
потому что я не хочу терять точность.
Решение
Вы не обязательно теряете точность с -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
.
Другие советы
Очень вероятно, потому что векторизация означает, что у вас могут быть разные результаты или могут означать, что вы пропускаете сигналы / исключения с плавающей точкой.
Если вы собираетесь для 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
может помочь также
Потому что -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.Автоматическая векторизация
Это может быть включено с помощью -fassociative-math
флаг в gcc