Почему icc генерирует странную сборку для простого main? - PullRequest
0 голосов
/ 03 сентября 2018

У меня есть простая программа :

int main()
{
    return 2*7;
}

и GCC, и clang с включенными оптимизациями счастливо генерируют двоичный файл с двумя инструкциями, но icc выдает причудливый вывод.

     push      rbp                                           #2.1
     mov       rbp, rsp                                      #2.1
     and       rsp, -128                                     #2.1
     sub       rsp, 128                                      #2.1
     xor       esi, esi                                      #2.1
     mov       edi, 3                                        #2.1
     call      __intel_new_feature_proc_init                 #2.1
     stmxcsr   DWORD PTR [rsp]                               #2.1
     mov       eax, 14                                       #3.12
     or        DWORD PTR [rsp], 32832                        #2.1
     ldmxcsr   DWORD PTR [rsp]                               #2.1
     mov       rsp, rbp                                      #3.12
     pop       rbp                                           #3.12
     ret

1 Ответ

0 голосов
/ 03 сентября 2018

Я не знаю, почему ICC выбирает выравнивание стека по 2 строкам кэша:

and       rsp, -128                                     #2.1
sub       rsp, 128                                      #2.1

Это интересно. Кэш L2 имеет предварительный выборщик смежных строк, который любит перетягивать пары строк (в выровненной по 128 байтам группе) в L2. Но стековый фрейм main обычно не используется интенсивно. Возможно, там есть важные переменные в некоторых программах. (Это также объясняет настройку rbp, чтобы сохранить старый RSP, чтобы он мог вернуться после ANDing. Gcc создает кадры стека с RBP в функциях, где он также выравнивает этот стек.)


Остальное, потому что main() особенный, а ICC включает -ffast-math по умолчанию . (Это один из «грязных» маленьких секретов Intel, позволяющий автоматически векторизовать больше кода с плавающей запятой из коробки).

Это включает в себя добавление кода в начало main для установки битов DAZ / FTZ в MXCSR (регистр состояния / управления SSE). См. Руководства Intel x86 для получения дополнительной информации об этих битах, но они на самом деле не сложны:

  • DAZ: Денормалы - это ноль: в качестве входных данных для инструкции SSE / AVX денормали рассматриваются как ноль.

  • FTZ: Flush To Zero: при округлении результата инструкции SSE / AVX субнормальные результаты сбрасываются в ноль.

относящиеся: Опция SSE «Денормалы нули»

( ISO C ++ запрещает программе перезванивать в main(), поэтому компиляторам разрешено помещать однократные данные в саму main вместо файлов запуска CRT . Gcc / clang с -ffast-math указано для связывания ссылки в файлах запуска CRT, которые устанавливают MXCSR, но при компиляции с помощью gcc / clang это влияет только на code-gen с точки зрения того, какие оптимизации разрешены, то есть обрабатывает FP add / mul как ассоциативную, когда разные временные значения означают это действительно не так. Это совершенно не связано с настройкой DAZ / FTZ).


Денормал используется здесь как синоним субнормального: значение FP с минимальным показателем степени и значением, где неявный начальный бит равен 0 вместо 1. т. Е. Значение с величиной, меньшей FLT_MIN или DBL_MIN, наименьшее представимое нормализованное float / double.

https://en.wikipedia.org/wiki/Denormal_number.


Инструкции, которые дают ненормальный результат, могут быть на намного медленнее: для оптимизации на задержку быстрый путь в некоторых аппаратных средствах предполагает нормализованные результаты и принимает помощь микрокода, если результат не может быть нормализован. Используйте perf stat -e fp_assist.any для подсчета таких событий.

Из превосходной серии статей Брюса Доусона о FP: Это не нормально - производительность нечетных операций . Кроме того:

Агнер Фог провел некоторое тестирование (см. Его microarch pdf ) и отчеты для Haswell / Broadwell:

Недостаточное и субнормальное

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

Хасвелл и Бродвелл имеют штраф около 124 часов. циклы во всех случаях, когда операция над нормальными числами дает субнормальный результат. Существует аналогичный штраф за умножение между нормальным и субнормальным числом, независимо от того, является ли результат нормальный или субнормальный. Там нет штраф за добавление нормального и ненормальное число, независимо от результата. Там нет штрафа для результатов переполнения, недостаточного заполнения, бесконечности или числа.

Штрафы за ненормальные числа избегаются, если "сбрасывать в ноль" режим и режим «денормали-нуля» установлены в MXCSR регистр.

Так что в некоторых случаях современные процессоры Intel избегают штрафов даже при ненормальных значениях, но

...