【问题标题】:FMA intrinsics not working: is it Hardware or Compiler?FMA 内在函数不起作用:是硬件还是编译器?
【发布时间】:2017-11-21 15:39:09
【问题描述】:

我正在尝试使用英特尔 FMA 内在函数,例如 _mm_fmadd_ps (__m128 a, __m128 b, __m128 c),以便在我的代码中获得更好的性能。

所以,首先,我做了一个小测试程序,看看它可以做什么以及如何使用它们。

#include <stdio.h>
#include <stdlib.h>
#include "xmmintrin.h"

int main()
{
   __m128 v1,v2,v3,vr;
   v1 = _mm_set_ps (5.0, 5.0, 5.0, 5.0);
   v2 = _mm_set_ps (2.0, 2.0, 2.0, 2.0);
   v3 = _mm_set_ps (3.0, 3.0, 3.0, 3.0);

   vr = _mm_fmadd_ps (v1, v2, v3);
}

我遇到了这个错误:

vr = 错误:从类型“int”分配给类型“__m128”时类型不兼容 vr = _mm_fmadd_ps (v1, v2, v3);

我认为可能是处理器功能不允许使用此类指令,所以我在互联网上查找了我的处理器型号 (Intel® Core™ i7-4700MQ Processor),发现它仅支持 SSE4.1/4.2 , AVX 2.0 内在函数对我来说有点奇怪!! 所以我查看了 proc/cpuinfo 文件和 flags 部分,我找到了 ** fma ** 标志。这是硬件方面令人困惑的部分。

至于软件,在互联网上进行了一些挖掘后,我使用了这个 makefile 选项,我希望这不是问题。

CC=gcc
CFLAGS=-g -c -Wall -O2 -mavx2 -mfma 

我在 Ubuntu 12.04 LTS 上使用 Eclipse,GCC 版本为 4.9.4 谢谢。

【问题讨论】:

  • 这是一个编译器错误。代码还没有开始运行,所以不可能是你的芯片没有支持。
  • @PaulR : 成功了....谢谢
  • 请注意,这段代码没有任何用处,因此当您在启用优化的情况下编译它 (-O2) 时,编译器会忽略所有这些代码并简单地发出代码以从 main (@987654323) 返回 0 @)。所以它会真正快速运行。 :-)

标签: c x86 simd intrinsics fma


【解决方案1】:

C 的一个怪癖是该语言表明编译器要假定一个以前没有见过的符号,如果你像函数一样调用它,则必须返回 int。由于您没有包含实际定义_mm_fmadd_ps 签名的标头,因此您会收到关于将int 转换为__m128 的奇怪错误。

内在函数头的原始组织是每个指令代都有一个唯一的头,所以你有:

mmintrin.h     The original MMX instruction set (deprecated for x64 native)
mm3dnow.h      The AMD 3D Now! instruction set (deprecated for x64 native)
emmintrin.h    SSE (i.e. single-precision 4-wide SIMD)
xmmintrin.h    SSE2 (i.e. double-precision and integer 4-wide SIMD)

之后,他们开始使用引入新指令的处理器架构的代号。

pmmintrin.h    SSE3 (the p stands for Prescott)
tmmintrin.h    Supplemental SSE3 (the t stands for Tejas)
smmintrin.h    SSE4.1 (not sure what the s is here for.
               They were added for Penryn but p
               was already used for Prescott)
nmmintrin.h    SSE4.2 (the n stands for Nehalem)
wmmintrin.h    AES (the w stands for Westmere)

这些天来,新指令集往往以ammintrin.h 用于 AMD 起源的东西(ABM、BMI、LWP、TBM、XOP、FMA4、SSE4a、SSE5)或immintrin.h 用于 Intel 起源的东西( AVX、FMA3、F16C、AVX2 等)。 AVX-512 在zmmintrin.h

旧系统不是特别直观,但新系统也不是。在immintrin.h 中定义了许多 AMD 指令子集,因为它们是相同的指令。在文档或标题中查找它确实是知道哪个内在函数在哪里的唯一方法。

对于英特尔this website 是一个很好的参考。否则,您需要查看 AMD 和/或 Intel 的开发人员指南。

您可能会发现this blog series of mine 很有用。

【讨论】:

  • 这个怪癖不适用于 C++ 和 C99+,因为there are no implicit int in those standards
  • 为什么不只包含x86intrin.h 并相应地设置您的构建标志?节省大量时间来记住所有这些疯狂、不直观的名字。
  • 一般来说,除非您确切知道它所支持的平台,否则您不应该使用任何内在函数。否则,您很容易最终编写的程序无法在您认为应该的那么多系统上运行。仅仅包括所有内容可能是多余的,您仍然需要查看每条指令才能真正理解它。
  • 确实如此。问题是,我记得指令助记符以及它们属于哪个指令集,而不是这些 ?mmintrin.h 名称。我想我也可以查一下,但我从来没有找到这样做的充分理由。
  • @ChuckWalbourn, x86intrin.h 用于在编译时定义硬件的 GCC 和 Clang,例如使用-msse4.2,因此您只能获得在编译时选择的内在函数。 x86intrin.h 是这些编译器的合理选择。对于 MSVC,您通常不定义硬件(VEX 编码和其他一些编码除外),因此它允许您使用您包含的任何内在函数。
【解决方案2】:

-mfma 可能看起来有点麻烦,但它的存在是有充分理由的。结果

_mm_add_ps(_mm_mul_ps(a, b), c)
_mm_fmadd_ps(a, b, c)

其实不一样。如果您编写的代码必须在您运行代码的所有机器上计算完全相同的结果(确定性),那么您可能需要禁用 fma!这就是为什么您需要在构建中使用 -fma 启用它的原因。

不过,至少它不像启用 avx512 的 SkyLake-X CPU 所需的六个编译标志那么糟糕:(

【讨论】:

  • 仅供参考,/arch:AVX2 暗示 Visual C++ 使用 FMA3。即使您使用 FMA3 内在函数,编译器也不会总是发出真正的 FMA3 指令,因为编译器会根据使用上下文确定正确的代码生成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-13
  • 1970-01-01
  • 2011-07-01
  • 2021-09-27
  • 2021-07-10
  • 1970-01-01
  • 2020-10-27
相关资源
最近更新 更多