【问题标题】:Automatically generate FMA instructions in MSVCMSVC中自动生成FMA指令
【发布时间】:2016-03-19 20:55:16
【问题描述】:

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 慢得多(如果实现不依赖于FMA 指令)。

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

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

更新

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

【问题讨论】:

  • 你可能需要使用compiler intrinsics functions
  • 我知道这些内在函数,但我对它们不感兴趣。我希望编译器自动生成指令,就像 GCC 和 Clang 一样。现在是 2016 年。此外,在很多情况下,您无法显式使用这些内在函数,因为 fused-multiply-add 不属于单个操作或函数,它来自多个内联优化表达式。
  • 祝你好运。根据我的经验,MS 并不关心编译器的那一部分。即使您使用内部函数,它也会为 FMA 指令生成一些非常糟糕的代码。如果您关心 Windows 上 FMA 的性能,请使用不同的编译器。 (ICC还不错)
  • 您是在寻找标量 FMA 还是压缩(矢量)FMA?从您的代码 sn-p (假设给定函数未内联) - MSVS 将无法生成矢量代码。如果 MSVS 只使用 FMA,当桌面上有矢量代码时,我不会感到惊讶。您是否尝试编写简单的数据处理循环,迭代地执行 FMA(确保所有数组都定义在同一个函数中)并使用 MSVS 编译它?

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


【解决方案1】:

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

事实证明,标志 /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 指令的行为,因为编译器不会将其视为内在的。根据他们的回复,它将在未来的更新中修复,以获得尽可能好的代码生成。

【讨论】:

  • 我不需要/GL。我认为您正在以 32 位模式编译。太傻了。
  • 问题没有提到 x64 并且在某些情况下由于依赖关系而无法在 64 位模式下编译。
  • 这个问题在 VS 2017 和 VS 2019 上修复了吗?
  • @Royi 从那个版本开始我就没有尝试过。
  • 我想这是在你上瘾之前clang:-)。
【解决方案2】:

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]

【讨论】:

  • 对我来说,使用 /fp:fast/arch:AVX2/O2 它编译为 fmulfadd
  • @plasmacel,这些是 x87 指令。您必须在 32 位模式下编译。在 64 位模式下编译。
猜你喜欢
  • 2015-12-15
  • 1970-01-01
  • 2020-07-06
  • 2018-06-13
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
  • 2023-03-15
  • 1970-01-01
相关资源
最近更新 更多