С GCC 5.3 следующий компилятор кода с -O3 -fma
float mul_add(float a, float b, float c) {
return a*b + c;
}
создает следующую сборку
vfmadd132ss %xmm1, %xmm2, %xmm0
ret
Я заметил, что GCC делает это с -O3
уже в GCC 4.8.
Clang 3.7 с -O3 -mfma
производит
vmulss %xmm1, %xmm0, %xmm0
vaddss %xmm2, %xmm0, %xmm0
retq
но Clang 3.7 с -Ofast -mfma
создает тот же код, что и GCC, с -O3 fast
.
Я удивлен, что GCC делает с -O3
, потому что из этого ответа он говорит
Компилятору не разрешается спланировать разделяемое добавление и умножение, если вы не разрешаете расслабленную модель с плавающей запятой.
Это связано с тем, что FMA имеет только одно округление, а ADD + MUL - два. Таким образом, компилятор будет нарушать строгие IEEE-операции с плавающей запятой путем слияния.
Однако из эта ссылка говорится
Независимо от значения FLT_EVAL_METHOD, любое выражение с плавающей запятой может быть сжато, то есть рассчитано так, как если бы все промежуточные результаты имели бесконечный диапазон и точность.
Итак, теперь я смущен и обеспокоен.
- Является ли GCC оправданным при использовании FMA с
-O3
? - Прерывает ли плавкое строковое поведение с плавающей запятой IEEE?
- Если fusing нарушает IEEE с плавающей точкой beahviour, и поскольку GCC возвращает
__STDC_IEC_559__
, это не противоречие?
Так как FMA можно эмулировать в программном обеспечении, похоже, должно быть два переключателя компилятора для FMA: один, чтобы сообщить компилятору использовать FMA в вычислениях, а один - сообщите компилятору, что аппаратное обеспечение имеет FMA.
Возможно, это можно контролировать с помощью опции -ffp-contract
. С GCC значением по умолчанию является -ffp-contract=fast
, а Clang - нет. Другие опции, такие как -ffp-contract=on
и -ffp-contract=off
, не производят инструкцию FMA.
Например, Clang 3.7 с -O3 -mfma -ffp-contract=fast
создает vfmadd132ss
.
Я проверил некоторые перестановки #pragma STDC FP_CONTRACT
, установленные на ON
и OFF
, с -ffp-contract
, установленными на ON
, OFF
и fast
. Во всех случаях я также использовал -O3 -mfma
.
С GCC ответ прост. #pragma STDC FP_CONTRACT
ВКЛ или ВЫКЛ не имеет значения. Только -ffp-contract
имеет значение.
GCC использует fma
с
-
-ffp-contract=fast
(по умолчанию).
В Clang используется fma
- с
-ffp-contract=fast
. - с
-ffp-contract=on
(по умолчанию) и#pragma STDC FP_CONTRACT ON
(по умолчанию -OFF
).
Другими словами, с помощью Clang вы можете получить fma
с помощью #pragma STDC FP_CONTRACT ON
(поскольку -ffp-contract=on
является значением по умолчанию) или с помощью -ffp-contract=fast
. -ffast-math
(и, следовательно, -Ofast
) установите -ffp-contract=fast
.
Я посмотрел в MSVC и ICC.
В MSVC используется команда fma с /O2 /arch:AVX2 /fp:fast
. С MSVC /fp:precise
по умолчанию.
С ICC он использует fma с -O3 -march=core-avx2
(acctually -O1
). Это связано с тем, что по умолчанию ICC использует -fp-model fast
. Но ICC использует fma даже с -fp-model precise
. Для отключения fma с использованием ICC используйте -fp-model strict
или -no-fma
.
Таким образом, по умолчанию GCC и ICC используют fma при включении fma (с -mfma
для GCC/Clang или -march=core-avx2
с ICC), но Clang и MSVC этого не делают.