Почему fetestexcept в C++ скомпилирован в вызов функции, а не встроен

Я оцениваю использование (очистка и запросы) исключений с плавающей запятой в критически важном для производительности/"горячем" коде. Глядя на полученный двоичный файл, я заметил, что ни GCC, ни Clang не расширяют вызов встроенной последовательностью инструкций, как я ожидал; вместо этого они, похоже, генерируют вызов библиотеки времени выполнения. Это непомерно дорого для моего приложения.

Рассмотрим следующий минимальный пример:

      #include <fenv.h>
#pragma STDC FENV_ACCESS on

inline int fetestexcept_inline(int e)
{
  unsigned int mxcsr;
  asm volatile ("vstmxcsr" " %0" : "=m" (*&mxcsr));
  return mxcsr & e & FE_ALL_EXCEPT;
}

double f1(double a)
{
    double r = a * a;
    if(r == 0 || fetestexcept_inline(FE_OVERFLOW)) return -1;
    else return r;
}

double f2(double a)
{
    double r = a * a;
    if(r == 0 || fetestexcept(FE_OVERFLOW)) return -1;
    else return r;
}

И вывод, произведенный GCC: https://godbolt.org/z/jxjzYY

Компилятор, кажется, знает, что он может использовать AVX-инструкции, зависящие от семейства ЦП, для цели (он использует «vmulsd» для умножения). Однако независимо от того, какие флаги оптимизации я пытаюсь использовать, он всегда будет производить гораздо более дорогой вызов функции glibc, а не сборку, которая (насколько я понимаю) должна делать то, что делает соответствующая функция glibc.

Это не жалоба, я согласен с добавлением встроенной сборки. Мне просто интересно, может ли быть тонкая разница, которую я упускаю из виду, которая может быть ошибкой в ​​​​версии встроенной сборки.

1 ответ

Это необходимо для поддержки арифметики. fetestexceptнеобходимо объединить состояния SSE и FPU, потому что long doubleоперации обновляют только состояние FPU, но не регистр MXSCR. Поэтому польза от инлайнинга несколько снижается.

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