Слитый умножить добавить и режимы округления по умолчанию

С 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, любое выражение с плавающей запятой может быть сжато, то есть вычислено так, как если бы все промежуточные результаты имели бесконечный диапазон и точность.

Так что теперь я смущен и обеспокоен.

  1. Оправдан ли GCC использование FMA с -O3?
  2. Не нарушает ли слияние строгое поведение с плавающей точкой IEEE?
  3. Если фьюзинг нарушает поведение 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 с

  1. -ffp-contract=fast (дефолт).

С Clang он использует fma

  1. с -ffp-contract=fast,
  2. с -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 полезна, если вам нужна не скорость, а точность. Он будет рассчитывать результат по контракту медленно, но правильно, даже если он не доступен в аппаратном обеспечении. И должен быть встроен, если он доступен в аппаратном обеспечении.

Другие вопросы по тегам