G CC проблема с -Ofast? - PullRequest
       8

G CC проблема с -Ofast?

5 голосов
/ 17 июня 2020

У меня вопрос о последних компиляторах G CC (версия> = 5) с таким кодом:

#include <math.h>

void test_nan (
    const float * const __restrict__ in,
    const int n,
    char * const __restrict__ out )
{
    for (int i = 0; i < n; ++i)
        out[i] = isnan(in[i]);
}

Листинг сборки из G CC:

test_nan:
        movq    %rdx, %rdi
        testl   %esi, %esi
        jle     .L1
        movslq  %esi, %rdx
        xorl    %esi, %esi
        jmp     memset
.L1:
        ret

Это похоже на memset(out, 0, n). Почему G CC предполагает, что никакие записи не могут быть NaN с -Ofast? При тех же параметрах компиляции I CC не показывает эту проблему. С G CC проблема устраняется с помощью «-O3».

Обратите внимание, что с «-O3» этот запрос gcc -c -Q -O3 --help=optimizers | egrep -i nan дает -fsignaling-nans [disabled].

Я проверил это как локально и на godbolt , с дополнительной опцией «-std = c99».

Edit: следуя полезным ответам ниже, я могу подтвердить, что -Ofast -std=c99 -fno-finite-math-only правильно решает эту проблему.

Ответы [ 2 ]

7 голосов
/ 17 июня 2020

Из документации G CC , которые управляют оптимизацией .

-Ofast включает следующие оптимизации в дополнение к -O3:

Он включает массивы -ffast-math , -fallow-store-data-races и Fortran-specifici c -fstack-array, если не указано -fmax-stack-var-size и -fno -protect-parens.

-ffast-math включает следующее:

-fno-math-errno, -funsafe-math-optimizations, -ffinite- math-only , -fno-rounding-math, -fno-signaling-nans, -fcx-limited-range и -fexcess-precision = fast.

-ffinite-math-only выполняет следующие действия :

Разрешить оптимизацию для арифметики с плавающей запятой c, которая предполагает, что аргументы и результаты не NaN или + -Infs.

Это позволяет предположить, что isnan() всегда возвращает 0.

2 голосов
/ 17 июня 2020

Ответ Бармара объясняет, почему -Ofast заставляет компилятор предполагать, что NaN никогда не бывает. У меня есть две вещи, которые нужно добавить к этому.

Во-первых, вы сказали что-то о том, что -fsignaling-nans [disabled] в --help=optimize выводится. Сигнализация NaN - это подкатегория всех битовых шаблонов NaN. ЦП выдает исключение с плавающей запятой, когда они используются (обратитесь к руководству по архитектуре, чтобы узнать, что именно означает «когда они используются»). Обычно люди используют только другой тип, quiet NaN, потому что иметь дело с исключениями с плавающей запятой - боль; поэтому по умолчанию G CC генерирует код, который обрабатывает тихие NaN (и ± Inf), но не сигнализирует NaN. isnan верно как для тихих, так и для сигнальных NaN. Короче говоря, -fsignaling-nans - отвлекающий маневр; вариант, который напрямую управляет поведением, которое вам не понравилось, - это -ffinite-math-only.

Во-вторых, если вы использовали -Ofast, потому что хотели, чтобы эта функция была векторизована, попробуйте вместо этого -O3 -march=native. Векторизация L oop включена в -O3, а -march=native указывает G CC на оптимизацию всех возможностей процессора, на котором он работает. Без каких-либо переключателей -march G CC будет считать, что он может использовать только функции ЦП, которые гарантированно доступны psABI; для x86-64 (как кажется, у вас есть) это SSE2, но не более того, что не учитывает большинство векторных возможностей. На компьютере, на котором я набираю это, -O3 -march=native создает код для вашей примерной функции, который вдвое меньше и , вероятно, примерно в четыре раза быстрее, чем -O3 в одиночку.

...