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% пиковых провалов процессора, предполагая, что 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

Редактировать:

Основываясь на комментариях Mysticial, я пошел дальше и использовал _mm256_fmadd_ps, и теперь версия AVX2 + FMA стала быстрее. Я не уверен, почему компилятор не сделает это для меня. Сейчас я получаю около 80 GFLOPS (110% пиковых флопов без FMA) для более 1000×1000 матриц. В случае, если кто-то не доверяет моему расчету пикового флопа, вот что я сделал.

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 с плавающей запятой, хотя, вероятно, это и лучше).

Редактировать:

Кто-то должен повторить
Как мне достичь теоретического максимума 4 FLOP за цикл?
но сделайте 8 двойных операций с плавающей запятой за цикл с Haswell.

редактировать

Фактически, Mysticial обновил свой проект для поддержки 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).

17

Решение

Только отвечая на очень небольшую часть вопроса здесь. Если ты пишешь _mm256_add_ps(_mm256_mul_ps(areg0,breg0), tmp0), gcc-4.9 обрабатывает его почти как встроенный ассемблер и не сильно его оптимизирует. Если вы замените его на areg0*breg0+tmp0синтаксис, который поддерживается как gcc, так и clang, затем gcc начинает оптимизацию и может использовать FMA, если доступно. я улучшил это для gcc-5, _mm256_add_ps например, теперь реализован как встроенная функция, которая просто использует +Таким образом, код с внутренними компонентами также может быть оптимизирован.

7

Другие решения

Следующие параметры компилятора достаточны для контракта _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.

3

По вопросам рекламы [email protected]