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) для матриц размером более 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, хотя, вероятно, это лучше).

Изменить:

Кому-то нужно повторить Как мне достичь теоретического максимума в 4 FLOP за цикл? но делать 8 двойных FLOPS с плавающей запятой за цикл с 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).


person Z boson    schedule 08.01.2014    source источник
comment
Проверьте сборку, чтобы узнать, действительно ли компилятор использует инструкции FMA. Если это не так, и вы уже попробовали кучу параметров компилятора (включая -ffast-math), тогда ваш единственный вариант - использовать встроенные функции вручную. (или просто используйте библиотеку, которая его использует) Другими словами, компилятор может быть недостаточно умен, чтобы генерировать FMA, даже если вы укажете -mfma и -ffast-math.   -  person Mysticial    schedule 08.01.2014
comment
Я последовал твоему совету @Mysticial и использовал _mm256_fmadd_ps, и теперь я получаю более 110% пиковых флопов (рассчитанных без FMA). Я получаю более 80 GFLOPS на моем маленьком 15-ваттном процессоре (в Intel NUC). Но я получаю небольшую ошибку при сравнении с моим базовым расчетом. Я предполагаю, что это связано с новым режимом округления FMA. Мне придется изменить то, с чем я сравниваю. Не знаю, как это сделать. С другой стороны, я не получаю почти такого большого прироста, как я ожидал (раньше я получал 80% пиковых флопов).   -  person Z boson    schedule 08.01.2014
comment
Это довольно типично для оптимизации. Когда вы делаете что-то быстрее, вы обнаруживаете новые узкие места. Так что вы, вероятно, больше не связаны вычислениями. Вы исчерпываете пропускную способность кеша? Пропускная способность вашего магазина?   -  person Mysticial    schedule 08.01.2014
comment
@Zboson: да, использование FMA немного повлияет на результаты (так же, как мозаика, векторизация и большинство других уловок, которые входят в быстрое умножение матриц). Обычно эти операции проверяют, проверяя, что относительная ошибка в некоторой матричной норме ограничена порогом, линейным по размеру матрицы.   -  person Stephen Canon    schedule 08.01.2014
comment
@Zboson Для второго редактирования щелкните проект github, на который я указал ссылку на вопрос о Flops. Недавно я обновил его, чтобы включить FMA3.   -  person Mysticial    schedule 08.01.2014
comment
@Mysticial, я думаю, что моя пропускная способность загрузочного хранилища в порядке (хотя это всего лишь предположение, потому что я думаю, что понимаю это). Вероятно, это проблема с кешем, поскольку кеш все еще меня немного сбивает с толку. Когда я компилирую в MSVC или ICC, получаю только 50% пикового числа флопов (без FMA), тогда как GCC получает 80% пика. Это странно. Стыдно признаться, что я до сих пор в основном отладчик printf. Как мне проверить пропускную способность моего кеша?   -  person Z boson    schedule 09.01.2014
comment
@Zboson Я никогда не пытался измерить пропускную способность кеша. Но вы, вероятно, можете сделать это довольно легко, копируя память с помощью SIMD для разных размеров данных. (Я тоже в основном отладчик печати.)   -  person Mysticial    schedule 09.01.2014
comment
@Mysticial, я запустил твой код провалов. В Linux вы не компилируете для fma. Я попытался исправить это, но получил результат выше, чем пиковое значение для fma. Я еще не заглядывал. Я перезагрузился в Windows, запустил ваш код флопа и отправил свои результаты на свой вопрос. Я получаю 95% пика с вашим кодом! Спасибо за ваш код. Там есть много драгоценных камней кода, которым я собираюсь научиться.   -  person Z boson    schedule 09.01.2014
comment
@Zboson Ах да. Я еще не обновлял скрипты сборки Linux. Но рад слышать, что вы, по крайней мере, получили версию для Windows, работающую так, как вам нравится. :)   -  person Mysticial    schedule 09.01.2014
comment
Вы показали свой код? Я не вижу этого, но предполагаю, что вы использовали встроенные функции, такие как _mm_mul_pd (a, b), вместо того, чтобы писать a * b, что объясняет, почему gcc не генерировал инструкции fma.   -  person Marc Glisse    schedule 08.02.2014
comment
@MarcGlisse, да, я использую встроенные функции. Что-то вроде _mm256_add_ps(_mm256_mul_ps(areg0,breg0), tmp0). Если я хочу, чтобы GCC выполнял fma, когда a и b являются регистрами AVX, как мне это сделать? Вы знаете об этом больше, чем я. Почему бы тебе не написать ответ?   -  person Z boson    schedule 08.02.2014
comment
Привет. Кажется, что вы внесли некоторые правки, которые меняют вопрос. Этикет редактирования, кажется, лучше всего резюмируется, если вы редактируете, чтобы сделать вещи лучше, яснее и эффективнее - никогда изменить смысл. Возможно, в будущем, если вы почувствуете необходимость задать другой вопрос, вы могли бы задать новый вопрос, а не изменять уже существующий вопрос?   -  person autistic    schedule 25.12.2015
comment
@seb, не знаю, как я изменил вопрос. Я в основном добавил к этому. Но в любом случае это было больше года назад. С тех пор я занимаюсь немного другим.   -  person Z boson    schedule 25.12.2015


Ответы (2)


Здесь отвечу только на очень небольшую часть вопроса. Если вы пишете _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 теперь реализован как встроенная функция, которая просто использует +, поэтому код с внутренними функциями также может быть оптимизирован.

person Marc Glisse    schedule 08.02.2014
comment
Я проверил ваше предложение и могу подтвердить, что он использует инструкцию fma. На самом деле мне даже не нужно было включать для этого быструю математику. - person Z boson; 10.02.2014
comment
эта запись ('areg0 * breg0 + tmp0') не будет работать с icc, я думаю. - person Walter; 08.07.2014
comment
Я только что видел твою правку. Спасибо за то, что реализовали это! - person Z boson; 27.12.2015
comment
Если кому-то еще интересно, соответствующий флаг не -ffast-math, это -ffp-contract = fast. - person Marc Glisse; 04.12.2016

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


Хотя -O1 достаточно для этой оптимизации с некоторыми компиляторами, всегда используйте не менее -O2 для общей производительности, предпочтительно -O3 -march=native -flto, а также оптимизацию на основе профиля.

И если это нормально для вашего кода, -ffast-math.

person Z boson    schedule 25.12.2015