Автоматически генерировать инструкции FMA в MSVC

MSVC уже много лет поддерживает инструкции AVX / AVX2 в соответствии с это сообщение в блоге msdn, он может автоматически генерировать fused-multiply-add (FMA) инструкция.

Однако ни одна из следующих функций не компилируется в инструкцию FMA:

float func1(float x, float y, float z)
{
    return x * y + z;
}

float func2(float x, float y, float z)
{
     return std::fma(x,y,z);
}

Хуже того, std :: fma не реализована как одна инструкция FMA, она работает ужасно, намного медленнее, чем простой x * y + z (ожидается низкая производительность std :: fma, если реализация не полагается на инструкцию FMA).

Я компилирую с /arch:AVX2 /O2 /Qvec флагами. Также пробовал с /fp:fast, безуспешно.

Итак, вопрос в том, как заставить MSVC автоматически выдавать инструкции FMA?

ОБНОВЛЕНИЕ

Существует #pragma fp_contract (on|off), который (похоже) ничего не делает.


person plasmacel    schedule 14.12.2015    source источник
comment
Возможно, вам потребуется использовать встроенные функции компилятора.   -  person Some programmer dude    schedule 14.12.2015
comment
Я знаю эти внутренние особенности, но они меня не интересуют. Я хочу, чтобы компилятор автоматически генерировал инструкции, как GCC и Clang. Это 2016 год. Более того, есть много случаев, когда вы не можете явно использовать эти инстринсики, потому что fused-multiply-add не относится к одной операции или функции, а исходит из нескольких встроенных оптимизированных выражений.   -  person plasmacel    schedule 14.12.2015
comment
Удачи. По моему опыту, MS не заботится об этой части компилятора. Даже когда вы используете встроенные функции, он генерирует довольно ужасный код для инструкций FMA. Если вам важна производительность FMA в Windows, используйте другой компилятор. (ICC довольно хорош)   -  person Mysticial    schedule 14.12.2015
comment
Честно говоря, MSVC не хватает многих современных функций, которые должны быть сегодня основными элементами компилятора. Не говоря уже о том, что он все время отстает от стандарта. Я был шокирован тем, что он не оптимизирует небольшие циклы, в которых количество итераций (скажем, 4) известно во время компиляции, и даже нет прагмы или чего-то еще, чтобы явно запросить это. Он по-прежнему реализует OpenMP 2.5, поэтому вы не можете использовать size_t для циклов omp, однако OpenMP 4.5 сейчас отсутствует. Он предлагает несколько расширенных наборов инструкций, но не генерирует для них правильный код. На самом деле я использую Clang для Windows, но также хотел выбрать что-то для MSVC   -  person plasmacel    schedule 14.12.2015
comment
Вы ищете скалярную FMA или упакованную (векторную) FMA? Из фрагмента кода (при условии, что указанные функции не встроены) - MSVS не сможет сгенерировать векторный код. Я не удивлюсь, если MSVS использует только FMA, когда в таблице есть векторный код. Вы пытались написать простой цикл обработки данных, итеративно выполняя FMA (убедившись, что все массивы определены в одной и той же функции), и скомпилировать его с помощью MSVS?   -  person zam    schedule 15.12.2015
comment
У меня это сработало с /O1 /arch:AVX2 /fp:fast с MSVC 2015.   -  person Z boson    schedule 04.01.2016


Ответы (2)


Я решил эту давнюю проблему.

Как оказалось, флагов /fp:fast, /arch:AVX2 и /O1 (или выше /O1) недостаточно для режима Visual Studio 2015 для выдачи инструкций FMA в 32-битном режиме. Вам также понадобится «Оптимизация всей программы» включен с флагом /GL.

Затем Visual Studio 2015 сгенерирует инструкцию FMA vfmadd213ss для

float func1(float x, float y, float z)
{
    return x * y + z;
}

Что касается std::fma, я открыл ошибка в Microsoft Connect. Они подтвердили, что std::fma не компилируется в инструкции FMA, потому что компилятор не рассматривает его как внутреннее. Согласно их ответу, это будет исправлено в будущем обновлении, чтобы получить лучший кодогенератор.

person plasmacel    schedule 08.04.2016
comment
Мне не нужен был /GL. Я думаю, вы компилируете в 32-битном режиме. Это глупо. - person Z boson; 09.04.2016
comment
В вопросе не упоминается x64, и в некоторых случаях его невозможно скомпилировать в 64-битном режиме из-за зависимостей. - person plasmacel; 09.04.2016
comment
Было ли это исправлено в VS 2017 и VS 2019? - person Royi; 03.05.2019
comment
@Royi Я не пробовал с той версии. - person plasmacel; 03.05.2019
comment
Я предполагаю, что это до того, как ты clang пристрастишься :-). - person Royi; 03.05.2019
comment
@Royi Совершенно верно. Теперь вы можете использовать официальное расширение LLVM Visual Studio для компиляции с помощью clang-cl, используя Visual Studio в качестве IDE. Или вы можете использовать проекты на основе CMake (компилируемые с помощью любого совместимого компилятора) в Visual Studio 2017 и 2019, что является полностью кроссплатформенным. - person plasmacel; 03.05.2019
comment
У меня проблема заключается в том, что у меня есть CLang с поддержкой OpenMP, о чем мы говорили ранее - chat.stackoverflow.com/rooms/170911. У вас есть руководство по его созданию? - person Royi; 03.05.2019
comment
@Royi Я думаю, двоичные файлы OpenMP теперь включены в официальные двоичные файлы LLVM Windows. По крайней мере, они были в LLVM 7.0. - person plasmacel; 03.05.2019
comment
Можем ли мы поговорить об этом в чате? Это меняет правила игры. - person Royi; 03.05.2019

MSVC 2015 действительно генерирует инструкцию fma для скалярных операций, но не для векторных операций (если вы явно не используете внутреннюю fma-команду).

Я скомпилировал следующий код

//foo.cpp
float mul_add(float a, float b, float c) {
    return a*b + c;
}

//MSVC cannot handle vectors as function parameters so use const references
__m256 mul_addv(__m256 const &a, __m256 const &b, __m256 const &c) {
    return _mm256_add_ps(_mm256_mul_ps(a, b), c);
}

с участием

cl /c /O2 /arch:AVX2 /fp:fast /FA foo.cpp

в MSVC2015 и произвел следующую сборку

;mul_add
vmovaps xmm3, xmm1
vfmadd213ss xmm3, xmm0, xmm2
vmovaps xmm0, xmm3

и

;mul_addv
vmovups ymm0, YMMWORD PTR [rcx]
vmulps  ymm1, ymm0, YMMWORD PTR [rdx]
vaddps  ymm0, ymm1, YMMWORD PTR [r8]
person Z boson    schedule 04.01.2016
comment
для меня, используя /fp:fast, /arch:AVX2 и /O2, он компилируется в fmul и fadd - person plasmacel; 08.04.2016
comment
@plasmacel, это инструкции x87. Вы должны компилировать в 32-битном режиме. Скомпилировать в 64-битном режиме. - person Z boson; 09.04.2016