9

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(如果实现不依赖于 FMA 指令,那么 std::fma 的性能会很差)。

/arch:AVX2 /O2 /Qvec我用标志编译。也试过了/fp:fast,没有成功。

那么问题来了,MSVC如何强制自动发出FMA指令呢?

更新

有一个#pragma fp_contract (on|off),它(看起来)什么都不做。

4

2 回答 2

4

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

事实证明,flags/fp:fast/arch:AVX2/O1或更高版本/O1)不足以让 Visual Studio 2015 模式在 32 位模式下发出 FMA 指令。您还需要使用 flag 打开“整个程序优化”/GL

然后 Visual Studio 2015 将生成一个 FMAvfmadd213ss指令

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

关于std::fma,我在 Microsoft Connect 中打开了一个错误。他们确认了std::fma无法编译为 FMA 指令的行为,因为编译器不会将其视为内在的。根据他们的回应,它将在未来的更新中得到修复,以获得最好的代码生成。

于 2016-04-08T15:44:55.653 回答
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]
于 2016-01-04T19:52:39.900 回答