在MSVC

时间:2015-12-14 11:32:26

标签: c++ visual-c++ x86 avx fma

MSVC多年来支持AVX / AVX2指令,根据this msdn blog post,它可以自动生成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),(看起来像)什么都不做。

2 个答案:

答案 0 :(得分:4)

我解决了这个长期存在的问题。

事实证明,标记/fp:fast/arch:AVX2/O1(或/O1以上)不足以使Visual Studio 2015模式在32-中发出FMA指令位模式。您还需要使用标记/GL打开"Whole Program Optimization"

然后Visual Studio 2015将为

生成FMA指令vfmadd213ss
float func1(float x, float y, float z)
{
    return x * y + z;
}

关于std::fma,我打开了bug at Microsoft Connect。他们确认了std::fma没有编译成FMA指令的行为,因为编译器并没有将其视为内在函数。根据他们的回复,它将在未来的更新中得到修复,以获得最佳代码。

答案 1 :(得分:3)

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]