Слитый умножить добавить и режимы округления по умолчанию
С 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.
Лязг 3.7 с -O3 -mfma
производит
vmulss %xmm1, %xmm0, %xmm0
vaddss %xmm2, %xmm0, %xmm0
retq
но лязг 3.7 с -Ofast -mfma
производит тот же код, что и GCC с -O3 fast
,
Я удивлен, что GCC делает с -O3
потому что из этого ответа он говорит
Компилятору не разрешается объединять разделенные сложения и умножения, если только вы не разрешите модель с плавающей запятой.
Это потому, что FMA имеет только одно округление, а ADD + MUL - два. Таким образом, компилятор будет нарушать строгое поведение IEEE с плавающей точкой путем слияния.
Однако по этой ссылке написано
Независимо от значения FLT_EVAL_METHOD, любое выражение с плавающей запятой может быть сжато, то есть вычислено так, как если бы все промежуточные результаты имели бесконечный диапазон и точность.
Так что теперь я смущен и обеспокоен.
- Оправдан ли GCC использование FMA с
-O3
? - Не нарушает ли слияние строгое поведение с плавающей точкой IEEE?
- Если фьюзинг нарушает поведение IEEE с плавающей точкой и 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 нет.
2 ответа
Это не нарушает IEEE-754, потому что IEEE-754 придерживается языков по этому вопросу:
Языковой стандарт должен также определять и требовать, чтобы реализации предоставляли атрибуты, которые разрешают и запрещают оптимизацию с изменением значения, отдельно или коллективно, для блока. Эти оптимизации могут включать, но не ограничиваются:
...
- Синтез операции fusedMultiplyAdd из умножения и сложения.
В стандартном С STDC FP_CONTRACT
Pragma предоставляет средства для управления этой оптимизацией, меняющей ценность. Таким образом, GCC имеет лицензию на выполнение слияния по умолчанию, если это позволяет отключить оптимизацию путем установки STDC FP_CONTRACT OFF
, Не поддерживать это означает не придерживаться стандарта C.
Когда вы указали, что допускается слияние с несколькими множителями, вы пропустили важное условие "если не включена прагма FP_CONTRACT". Это новая функция в C (я думаю, она была представлена в C99), и она была абсолютно необходима PowerPC, которая с самого начала объединила функцию умножения-сложения - фактически, x*y был эквивалентен fma (x, y, 0) и x+y было эквивалентно fma (1.0, x, y).
FP_CONTRACT - это то, что элементы управления умножают / добавляют, а не FLT_EVAL_METHOD. Хотя если FLT_EVAL_METHOD допускает более высокую точность, то заключение контрактов всегда допустимо; просто сделайте вид, что операции были выполнены с очень высокой точностью, а затем округлены.
Функция fma полезна, если вам нужна не скорость, а точность. Он будет рассчитывать результат по контракту медленно, но правильно, даже если он не доступен в аппаратном обеспечении. И должен быть встроен, если он доступен в аппаратном обеспечении.