MSVC中自动生成FMA指令
Automatically generate FMA instructions in MSVC
MSVC 多年来一直支持 AVX/AVX2 指令,并且根据 this msdn blog post, it can automatically generate 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 指令)。
我用 /arch:AVX2 /O2 /Qvec
标志编译。
也用 /fp:fast
试过,没有成功。
所以问题是MSVC如何强制自动发出FMA指令?
更新
有一个#pragma fp_contract (on|off)
,它(看起来)什么都不做。
MSVC 2015 确实会为标量运算生成 fma 指令,但不会为向量运算生成 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]
我解决了这个长期存在的问题
事实证明,标志 /fp:fast
、/arch:AVX2
和 /O1
(或更高 /O1
)不足以让 Visual Studio 2015 模式发出32 位模式下的 FMA 指令。您还需要使用 /GL
.
标志打开 "Whole Program Optimization"
那么Visual Studio 2015 会生成一条FMA指令vfmadd213ss
for
float func1(float x, float y, float z)
{
return x * y + z;
}
关于std::fma
,我开了一个bug at Microsoft Connect。他们确认了 std::fma
不会编译为 FMA 指令的行为,因为编译器不会将其视为内在指令。根据他们的回应,它将在未来的更新中得到修复,以获得尽可能好的代码生成器。
MSVC 多年来一直支持 AVX/AVX2 指令,并且根据 this msdn blog post, it can automatically generate 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 指令)。
我用 /arch:AVX2 /O2 /Qvec
标志编译。
也用 /fp:fast
试过,没有成功。
所以问题是MSVC如何强制自动发出FMA指令?
更新
有一个#pragma fp_contract (on|off)
,它(看起来)什么都不做。
MSVC 2015 确实会为标量运算生成 fma 指令,但不会为向量运算生成 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]
我解决了这个长期存在的问题
事实证明,标志 /fp:fast
、/arch:AVX2
和 /O1
(或更高 /O1
)不足以让 Visual Studio 2015 模式发出32 位模式下的 FMA 指令。您还需要使用 /GL
.
那么Visual Studio 2015 会生成一条FMA指令vfmadd213ss
for
float func1(float x, float y, float z)
{
return x * y + z;
}
关于std::fma
,我开了一个bug at Microsoft Connect。他们确认了 std::fma
不会编译为 FMA 指令的行为,因为编译器不会将其视为内在指令。根据他们的回应,它将在未来的更新中得到修复,以获得尽可能好的代码生成器。