Любопытный результат поведения компоновщика gcc вокруг -ffast-math

Я заметил интересное явление вокруг флагов компоновщика компилятора, влияющих на работающий код способами, которые я не могу понять.

У меня есть библиотека, в которой представлены разные реализации одного и того же алгоритма, чтобы проверить скорость работы этих разных реализаций.

Сначала я протестировал ситуацию с парой идентичных реализаций, чтобы убедиться, что все произошло правильно (обе работали примерно с одинаковой скоростью). Я начал с компиляции объектов (по одному на реализацию) со следующими флагами компилятора:

-g -funroll-loops -flto -Ofast -Werror

а затем при линковке передал gcc следующие флаги:

-Ofast -flto=4 -fuse-linker-plugin

Это дало библиотеку, которая работала молниеносно быстро, но, что любопытно, была надежно и повторяемо ~ 7% быстрее для первого объекта, который был включен в аргументы во время компоновки (поэтому любая реализация была бы быстрее, если бы она была связана первой).

так с:

gcc -o libfoo.so -O3 -ffast-math -flto=4 -fuse-linker-plugin -shared support_obj.os obj1.os obj2.os -lm

vs

gcc -o libfoo.so -O3 -ffast-math -flto=4 -fuse-linker-plugin -shared support_obj.os obj2.os obj1.os -lm

в первом случае реализация в obj1 работала быстрее, чем реализация в obj2. Во втором случае было верно обратное. Чтобы было ясно, код идентичен в обоих случаях, за исключением имени записи функции.

Теперь я удалил эту странную разницу в порядке ссылок и аргументов (и фактически немного ускорил ее), удалив флаг -Ofast во время связывания.

Я могу воспроизвести в основном ту же ситуацию, изменив -Ofast на -O3 -ffast-math, но в этом случае мне нужно указать -ffast-math во время связывания, что снова приводит к странной разнице в скорости упорядочения. Я не уверен, почему ускорение поддерживается для -Ofast, но не для -ffast-math, когда -ffast-math не передается во время компоновки, но я могу допустить, что это может быть связано с оптимизацией времени компоновки, передающей соответствующую информацию в одном случае, но не в другом. . Однако это не объясняет разницы в скорости.

Удаление -ffast-math означает, что он работает примерно в 8 раз медленнее.

Кто-нибудь может пролить свет на то, что может происходить, чтобы вызвать этот эффект? Мне очень интересно узнать, что может вызвать такое забавное поведение, чтобы я не мог случайно вызвать его в будущем.

Тест скорости выполнения выполняется в python с использованием оболочки вокруг библиотеки и timeit, и я почти уверен, что это правильно (я могу менять порядок и вещи, чтобы показать, что побочные эффекты python незначительны).

Я также проверил библиотеку на правильность вывода, так что я тоже могу быть достаточно уверен в этом.


person Henry Gomersall    schedule 18.12.2015    source источник
comment
Не могли бы вы сделать минимально воспроизводимый пример или показать нам код вашей библиотеки? Трудно найти источник странного поведения, не имея возможности увидеть код, который его содержит.   -  person fuz    schedule 18.12.2015
comment
С какой версией GCC вы это наблюдаете?   -  person John Bollinger    schedule 18.12.2015
comment
Обратите внимание, что хотя -Ofast включает все оптимизации, описанные в -O3 и -ffast-math, документация не исключает возможности того, что она также включает другие оптимизации, которые ими не охвачены - возможно, даже те, которые недоступны каким-либо другим образом. В той мере, в какой ваш вопрос заключается в том, почему поведение -Ofast отличается от поведения -O3 -ffast-math, это, вероятно, как-то связано с этим.   -  person John Bollinger    schedule 18.12.2015
comment
Коллекция компиляторов GNU не включает компоновщик, но использует системный компоновщик (обычно GNU ld один из binutils   -  person too honest for this site    schedule 18.12.2015
comment
Посмотрите на сгенерированный ассемблерный код. Это вполне возможно из-за того, как функции встроены в LTO, как организована память (например, выравнивание кеша), распределение регистров и т. д. Полный анализ здесь невозможен, поскольку он, вероятно, требует проверки кода.   -  person too honest for this site    schedule 18.12.2015
comment
Потерпите меня, я работаю над минимальным полным примером. Проблема заключается в уровне интеграции в код поддержки, из-за которого его немного сложно разобрать.   -  person Henry Gomersall    schedule 18.12.2015
comment
Итак, это очень сложно определить - изменение кода меняет проявление проблемы, поэтому создание минимального полного примера является сложной задачей. Я переместил все выделения памяти инициализации, чтобы получить выровненные данные, а также установил -mtune=native, что, кажется, в значительной степени устраняет любую разницу (хотя все еще ~ 2% или около того). Устанавливает ли mtune выравнивание стека? Какие параметры могут привести к тому, что одинаковые биты c не будут работать с одинаковой скоростью? (Стоит отметить, что сборка в обоих случаях не совсем идентична, хотя и близка).   -  person Henry Gomersall    schedule 18.12.2015


Ответы (1)


слишком длинный для комментария, опубликованного как ответ:

Из-за риска получения некорректных результатов при математических операциях я бы не советовал его использовать.

использование -ffast_math и/или -Ofast может привести к неправильным результатам, как показано в этих выдержках из руководства gcc:

option:-ffast-math Устанавливает параметры:

  1. -фно-математика-ошибка,
  2. -funsafe-математическая-оптимизация,
  3. -конечная-математика-только,
  4. -fno-округление-математика,
  5. -fno-signaling-nans и
  6. -fcx-ограниченный-диапазон.

Эта опция определяет макрос препроцессора __FAST_MATH__.

Этот параметр не включается никаким параметром -O, кроме -Ofast, поскольку это может привести к неправильному выводу для программ, зависящих от точной реализации правил/спецификаций IEEE или ISO для математических функций. Однако это может дать более быстрый код для программ, которые не требуют гарантий этих спецификаций. "

вариант: -Ofast

Не обращайте внимания на строгое соблюдение стандартов. -Ofast включает все -O3 оптимизации. Он также позволяет выполнять оптимизации, которые не подходят для всех программ, совместимых со стандартами. Он включает -ffast-math и специфичные для Fortran -fno-protect-parens и -fstack-arrays.

person user3629249    schedule 19.12.2015
comment
Что ж, все проблемы можно разумно принять, если их понять - многие из крайних случаев с плавающей запятой IEEE можно безопасно игнорировать во многих алгоритмах, но для этого все еще требуется -funsafe-math-optimizations. - person Henry Gomersall; 21.12.2015
comment
небезопасно означает не строгое соблюдение стандартов. - person Henry Gomersall; 21.12.2015