Как использовать инструкции Fused Multiply-Add (FMA) с SSE/AVX

Я узнал, что некоторые процессоры Intel/AMD могут выполнять одновременное умножение и добавление с помощью SSE / AVX:
FLOPS за цикл для песчаных мостов и скважин SSE2 / AVX / AVX2.

Мне нравится знать, как сделать это лучше всего в коде, и я также хочу знать, как это делается внутри процессора. Я имею в виду суперскалярную архитектуру. Допустим, я хочу сделать длинную сумму, такую ​​как следующее в SSE:

//sum = a1*b1 + a2*b2 + a3*b3 +... where a is a scalar and b is a SIMD vector (e.g. from matrix multiplication)
sum = _mm_set1_ps(0.0f);
a1  = _mm_set1_ps(a[0]); 
b1  = _mm_load_ps(&b[0]);
sum = _mm_add_ps(sum, _mm_mul_ps(a1, b1));

a2  = _mm_set1_ps(a[1]); 
b2  = _mm_load_ps(&b[4]);
sum = _mm_add_ps(sum, _mm_mul_ps(a2, b2));

a3  = _mm_set1_ps(a[2]); 
b3  = _mm_load_ps(&b[8]);
sum = _mm_add_ps(sum, _mm_mul_ps(a3, b3));
...

Мой вопрос, как это преобразуется в одновременное умножение и сложение? Могут ли данные быть зависимыми? Я имею в виду процессор может сделать _mm_add_ps(sum, _mm_mul_ps(a1, b1)) одновременно или регистры, используемые в умножении и сложении, должны быть независимыми?

Наконец, как это относится к FMA (с Haswell)? Является _mm_add_ps(sum, _mm_mul_ps(a1, b1)) автоматически конвертируется в одну инструкцию FMA или микрооперацию?

2 ответа

Решение

Компилятору разрешено объединять отдельные операции сложения и умножения, даже если это меняет конечный результат (делая его более точным).

FMA имеет только одно округление (оно эффективно сохраняет бесконечную точность для внутреннего результата временного умножения), в то время как ADD + MUL имеет два.

Стандарты IEEE и C позволяют это, когда #pragma STDC FP_CONTRACT ON в силе, и компиляторы могут иметь его ON по умолчанию (но не все так делают). Gcc сжимается в FMA по умолчанию (по умолчанию -std=gnu*, но нет -std=c* например, -std=c++14). Для Clang он включен только с -ffp-contract=fast, (Только с #pragma включен, только в пределах одного выражения, как a+b*c, а не через отдельные операторы C++.).

Это отличается от строгой и смягченной плавающей запятой (или в терминах GCC, -ffast-math против -fno-fast-math), что позволит другие виды оптимизации, которые могут увеличить ошибку округления в зависимости от входных значений. Этот особенный из-за бесконечной точности внутреннего временного FMA; если во внутреннем временном коде вообще было какое-либо округление, это не было бы разрешено в строгом FP.

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


Таким образом, лучший способ убедиться, что вы на самом деле получаете инструкции FMA, которые вы хотите, - это использовать предоставленные им встроенные функции:

Встроенные функции FMA3: (AVX2 - Intel Haswell)

  • _mm_fmadd_pd(), _ mm256_fmadd_pd()
  • _mm_fmadd_ps(), _mm256_fmadd_ps()
  • а еще около миллиарда других вариаций...

Встроенные функции FMA4: (XOP - AMD Bulldozer)

  • _mm_macc_pd(), _mm256_macc_pd()
  • _mm_macc_ps(), _mm256_macc_ps()
  • а еще около миллиарда других вариаций...

Я протестировал следующий код в GCC 5.3, Clang 3.7, ICC 13.0.1 и MSVC 2015 (версия компилятора 19.00).

float mul_add(float a, float b, float c) {
    return a*b + c;
}

__m256 mul_addv(__m256 a, __m256 b, __m256 c) {
    return _mm256_add_ps(_mm256_mul_ps(a, b), c);
}

С правильными параметрами компилятора (см. Ниже) каждый компилятор сгенерирует vfmadd инструкция (например, vfmadd213ss) от mul_add, Тем не менее, только MSVC не может заключить контракт mul_addv одному vfmadd инструкция (например, vfmadd213ps).

Следующие параметры компилятора достаточны для генерации vfmadd инструкции (кроме mul_addv с MSVC).

GCC:   -O2 -mavx2 -mfma
Clang: -O1 -mavx2 -mfma -ffp-contract=fast
ICC:   -O1 -march=core-avx2
MSVC:  /O1 /arch:AVX2 /fp:fast

GCC 4.9 не будет заключать контракт mul_addv до одной инструкции FMA, но, по крайней мере, GCC 5.1 это делает. Я не знаю, когда другие компиляторы начали это делать.

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