Что делает gcc ffast-math на самом деле?
Я понимаю, что флаг gcc --ffast-math
может значительно увеличить скорость для операций с плавающей запятой и выходит за рамки стандартов IEEE, но я не могу найти информацию о том, что происходит на самом деле. Может кто-нибудь объяснить некоторые детали и, возможно, дать ясный пример того, как что-то изменится, если флаг включен или выключен?
Я попытался прорваться через S.O. для подобных вопросов, но не смог найти ничего, объясняющего работу ffast-math.
Ответы
Ответ 1
Как вы упомянули, он позволяет оптимизировать, не сохраняя строгого соответствия IEEE.
Пример:
x = x*x*x*x*x*x*x*x;
к
x *= x;
x *= x;
x *= x;
Поскольку арифметика с плавающей запятой не является ассоциативной, упорядочение и факторинг операций повлияют на результаты за счет округления. Поэтому эта оптимизация не выполняется при строгом FP-поведении.
EDIT: на самом деле я не проверял, действительно ли GCC делает эту конкретную оптимизацию. Но идея такая же.
Ответ 2
-ffast-math
делает намного больше, чем просто нарушает строгое соответствие IEEE.
Прежде всего, конечно, это нарушает строгое соответствие IEEE, разрешая, например, переупорядочение инструкций для чего-то математически одного и того же (в идеале), но не то же самое в плавающей точке.
Во-вторых, он отключает настройку errno
после математических команд с одной инструкцией, что означает отказ от записи в локальную переменную потока (это может сделать 100% -ную разницу для этих функций на некоторых архитектурах).
В-третьих, он делает предположение, что всякая математика конечна, а это означает, что проверки NaN (или нуля) не выполняются, где они будут иметь пагубные последствия. Просто предполагается, что этого не произойдет.
В-четвертых, это позволяет взаимные приближения для деления и обратного квадратного корня.
Кроме того, он отключает подписанный ноль (код предполагает, что подписанный ноль не существует, даже если его поддерживает) и математика округления, которая позволяет, помимо прочего, постоянно складывать во время компиляции.
Наконец, он генерирует код, предполагающий, что аппаратные прерывания не могут произойти из-за математики сигнализации/захвата (т.е. если они не могут быть отключены в целевой архитектуре и, следовательно, они не будут обработаны).