2010-05-17 5 views
4

Почему необходимо использовать -ffast-math с g ++ для достижения векторизации циклов с использованием double? Мне не нравится -ffast-math, потому что я не хочу терять точность.Автоматическая векторизация на двойном и ffast-математике

+1

'-ffast-math' на самом деле является комбинационным флагом, который устанавливает набор других флагов, которые могут быть включены индивидуально, а вместо этого - возможно, вам удастся уйти с установкой только одного или двух отдельных флагов? – Amber

+1

Я пытался, но только с '--fast-math' я получаю максимальное число векторизованных циклов –

ответ

8

Вы не обязательно теряете точность с -ffast-math. Это влияет только на обработку NaN, Inf и т. Д. И порядок выполнения операций.

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

Например, следующий код выполняет операцию округления по f. Тем не менее, два f += g и f -= g операции, вероятно, получить оптимизированный прочь НКУ:

static double moo(double f, double g)          
{                   
    g *= 4503599627370496.0; // 2 ** 52          
    f += g;                 
    f -= g;                 
    return f;                
}                  

На x86_64, вы можете использовать этот asm заявление поручить GCC не выполнять эту оптимизацию:

static double moo(double f, double g)          
{                   
    g *= 4503599627370496.0; // 2 ** 52          
    f += g;                 
    __asm__("" : "+x" (f)); 
    f -= g; 
    return f; 
} 

Вы к сожалению, потребуется адаптировать это для каждой архитектуры. На PowerPC используйте +f вместо +x.

+0

Простое выполнение 'f'' volatile double' должно иметь такой же эффект. –

+0

Не совсем так, к сожалению. Существует побочный эффект использования 'volatile', который состоит в том, что переменная всегда записывается в память. В этом примере все операции могут выполняться в регистрах, но с 'volatile' компилятор выдаст несколько дополнительных кодов операций чтения/записи. Поэтому, даже если результат будет таким же, код будет намного медленнее. –

+0

Спасибо за объяснение Сэм! –

2

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

Если вы компилируете для 32-битного x86, то gcc и g ++ по умолчанию используют x87 для математики с плавающей запятой, а на 64-битном они по умолчанию - sse, однако x87 может и будет производить разные значения для одного и того же вычисления, вряд ли g ++ рассмотрит векторизация, если он не может гарантировать, что вы получите те же результаты, если вы не используете -ffast-math или некоторые из флагов, которые он включает.

В основном это сводится к точке среды с плавающей запятой для векторизованного кода не может быть такой же, как и для отсутствия векторизованного кода, иногда в отношениях, которые являются важными, если различия не имеют значения для вас, что-то вроде

-fno-math-errno -fno-trapping-math -fno-signaling-nans -fno-rounding-math 

но сначала найдите эти параметры и убедитесь, что они не повлияют на правильность вашей программы. -ffinite-math-only может также помочь

+0

Я думаю, что gcc на самом деле не слишком осторожно относится к окклюзионному режиму x87 и SSE и другим настройкам (например, denormal FTZ и DAZ) , Связано: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=34678: изменение режима округления между операциями FP требует '#pragma STDC FENV_ACCESS ON', и даже тогда gcc не полностью поддерживает это. Но я думаю, что gcc обычно не предполагает, что режим является округлым до ближайшего при оптимизации кода, который сам по себе не меняет режим округления. –

+0

@PeterCordes на x87 нет возможности делать определенные вещи с точно округленным поплавком/двойным только 80-разрядным расширенным типом x87. Не помните, что я узнал об этом, читая статью несколько лет назад. gcc ДЕЙСТВИТЕЛЬНО избегает тех вещей, которые он использовал, чтобы не иметь постоянной точки с плавающей точкой, потому что кто-то где-то может перекрестно компилироваться на что-то странное, а результаты не будут одинаковыми, в те дни у них есть внутренняя библиотека, которая может имитировать странные платформы с плавающей запятой поэтому он будет постоянно складываться, пока результат не будет отличаться. – Spudd86

+0

No * good * way, во всяком случае. Там 'gcc -ffloat-store', который хранит/перезагружает до раунда до' float' или 'double', но, конечно, это стоит больших объемов производительности. Кроме того, x87 имеет бит контроля точности, который вы можете установить для ограничения точности мантиссы до 53 или 24 (для более быстрого div/sqrt) вместо 64. См. Https://randomascii.wordpress.com/2012/03/21/ промежуточная точность с плавающей точкой /: по-видимому, MSVC используется для фактического снижения его точности! –

0

Потому что быстрая математика позволяет операции переупорядочения, что позволяет много векторизовать код.

Например, чтобы вычислить это

sum = a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + . . . a[99] 

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

Однако, если -ffast-math включен, то выражение может быть вычислено like this (Посмотрите на A7. Auto-Vectorization)

sum0 = a[0] + a[4] + a[ 8] + . . . a[96] 
sum1 = a[1] + a[5] + a[ 9] + . . . a[97] 
sum2 = a[2] + a[6] + a[10] + . . . a[98] 
sum3 = a[3] + a[7] + a[11] + . . . a[99] 
sum’ = sum0 + sum1 + sum2 + sum3 

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