FMA3 в GCC: как включить
У меня есть i5-4250U, у которого есть AVX2 и FMA3. Я тестирую некоторый плотный код умножения матрицы в GCC 4.8.1 на Linux, который я написал. Ниже приведен список трех разностных способов, которые я собираю.
SSE2: gcc matrix.cpp -o matrix_gcc -O3 -msse2 -fopenmp
AVX: gcc matrix.cpp -o matrix_gcc -O3 -mavx -fopenmp
AVX2+FMA: gcc matrix.cpp -o matrix_gcc -O3 -march=native -fopenmp -ffast-math
Версия SSE2 и AVX явно отличаются по производительности. Однако AVX2 + FMA не лучше версии AVX. Я этого не понимаю. Я получаю более 80% пиковых значений CPU, предполагая, что FMA нет, но я думаю, что с FMA я смогу сделать намного лучше. Матричное умножение должно быть полезно непосредственно из FMA. Я в основном делаю восемь точечных продуктов сразу в AVX. Когда я проверяю march=native
, он дает:
cc -march=native -E -v - </dev/null 2>&1 | grep cc1 | grep fma
...-march=core-avx2 -mavx -mavx2 -mfma -mno-fma4 -msse4.2 -msse4.1 ...
Итак, я вижу, что он включен (просто чтобы я добавил -mfma
, но это не имеет значения). ffast-math
должен разрешить непринужденную модель с плавающей запятой Как использовать команды Fused Multiply-Add (FMA) с SSE/AVX
Edit:
Основываясь на комментариях Mystical, я пошел вперед и использовал _mm256_fmadd_ps, и теперь версия AVX2 + FMA работает быстрее. Я не уверен, почему компилятор не сделает этого для меня. Теперь я получаю около 80 GFLOPS (110% пиковых провалов без FMA) для матриц более 1000x1000. В случае, если кто-то не доверяет моему расчету с пиковым флопом, вот что я сделал.
peak flops (no FMA) = frequency * simd_width * ILP * cores
= 2.3GHZ * 8 * 2 * 2 = 73.2 GFLOPS
peak flops (with FMA) = 2 * peak flops (no FMA) = 146.2 GFLOPS
Мой процессор в турборежиме при использовании обоих ядер равен 2,3 ГГц. Я получаю 2 для ILP, потому что Ivy Bridge может делать одно AVX-умножение и одно добавление AVX в одно и то же время (и я несколько раз развернул цикл для обеспечения этого).
Я получаю около 55% пиковых провалов (с FMA). Я не уверен, почему, но, по крайней мере, я сейчас что-то вижу.
Один побочный эффект заключается в том, что теперь я получаю небольшую ошибку, когда сравниваю простой алгоритм умножения матрицы. Я думаю, что из-за того, что FMA имеет только один режим округления вместо того, что обычно будет два (что иронически нарушает правила с плавающей точкой IEEE, хотя, вероятно, и лучше).
Edit:
Кому-то нужно переделать
Как достичь теоретического максимума из 4 FLOP за цикл?
но сделать 8 двойных плавающих точек FLOPS за цикл с Haswell.
Изменить
Собственно, Mystical обновил свой проект, чтобы поддержать FMA3 (см. его ответ в ссылке выше).
Я запустил его код в Windows8 с MSVC2012 (потому что версия Linux не скомпилировалась с поддержкой FMA). Вот результаты.
Testing AVX Mul + Add:
Seconds = 22.7417
FP Ops = 768000000000
FLOPs = 3.37705e+010
sum = 17.8122
Testing FMA3 FMA:
Seconds = 22.1389
FP Ops = 1536000000000
FLOPs = 6.938e+010
sum = 333.309
Это 69.38 GFLOPS для FMA3 для двойной с плавающей запятой. Для одиночной с плавающей запятой мне нужно удвоить ее так, чтобы 138.76 SP GFLOPS. Я рассчитываю, что мой пик равен 146,2 SP GFLOPS. Это 95% пика! Другими словами, я должен немного улучшить код GEMM (хотя он уже совсем немного быстрее, чем Eigen).
Ответы
Ответ 1
Отвечаю только на небольшую часть вопроса. Если вы пишете _mm256_add_ps(_mm256_mul_ps(areg0,breg0), tmp0)
, gcc-4.9 обрабатывает его почти как встроенный asm и не оптимизирует его. Если вы замените его на areg0*breg0+tmp0
, синтаксис, который поддерживается как gcc, так и clang, затем gcc начинает оптимизацию и может использовать FMA, если он доступен. я улучшено, что для gcc-5, _mm256_add_ps
, например, теперь реализовано как встроенная функция, которая просто использует +
, поэтому код с также могут быть оптимизированы внутренние функции.
Ответ 2
Следующие параметры компилятора достаточны для того, чтобы заключить контракт _mm256_add_ps(_mm256_mul_ps(a, b), c)
в одну инструкцию fma (например, vfmadd213ps
):
GCC 5.3: -O2 -mavx2 -mfma
Clang 3.7: -O1 -mavx2 -mfma -ffp-contract=fast
ICC 13: -O1 -march=core-avx2
Я пробовал /O2 /arch:AVX2 /fp:fast
с MSVC, но он все еще не сжимается (неожиданное удивление). MSVC выполнит скалярные операции, хотя.
GCC начал делать это, по крайней мере, с GCC 5.1.