x86 SIMD指令的头文件

173

哪些头文件提供了不同的x86 SIMD指令集扩展(MMX,SSE,AVX等)的内置函数?似乎很难在线上找到这样的列表。如果我错了,请纠正我。

5个回答

229
现在你通常只需包含<immintrin.h>,它会包含所有内容。
GCC和clang将阻止您使用未在编译时启用的指令内部函数(例如使用 -march=native-mavx2 -mbmi2 -mpopcnt -mfma -mcx16 -mtune=znver1等)。
MSVC和ICC将允许您在编译时不启用任何东西就可以使用内部函数,但使用AVX内部函数之前仍应该启用AVX。
历史上(在immintrin.h引入所有内容之前),您需要手动包含一个头文件以获取所需级别的内部函数。
对于MSVC和ICC,这仍然可能很有用,以防止您使用不想要的指令集。
<mmintrin.h>  MMX
<xmmintrin.h> SSE
<emmintrin.h> SSE2
<pmmintrin.h> SSE3
<tmmintrin.h> SSSE3
<smmintrin.h> SSE4.1
<nmmintrin.h> SSE4.2
<ammintrin.h> SSE4A
<wmmintrin.h> AES
<immintrin.h> AVX, AVX2, FMA

包含这些头文件中的任意一个会包含之前的所有头文件(除了仅限于AMD的SSE4A: immintrin.h不会包含它)。

一些编译器还有用于AVX512的<zmmintrin.h>


72
或者您可以只需#include <x86intrin.h>,这将引入您所需的所有内容。 - Paul R
2
zmmintrin.h 包含 AVX-512 内在函数。 - onitake
6
为何SSE3/SSSE3/SSE4.1和4.2要使用p、t、s和n?这些字符代表什么意思? - phuclv
8
@LưuVĩnhPhúc:SSE3代表Prescott新指令集,SSSE3代表Tejas新指令集。我认为SSE4.2和AES是指它们被引入的处理器家族(Nehalem和Westmere)。Translated: @LưuVĩnhPhúc: SSE3 represents the Prescott new instruction set, and SSSE3 represents the Tejas new instruction set. I think SSE4.2 and AES refer to the processor family they were introduced on (Nehalem and Westmere). - Drew McGowen
18
不要直接包含<zmmintrin.h>,因为gcc甚至不提供它。**只需使用<immintrin.h>**或者更加完整的<x86intrin.h>。这个答案基本上已经过时了,除非你有意避免包含新版本SSE的内置函数,因为当你编译为SSE2时,编译器不会在你使用SSE4.1指令时发出警告。(gcc/clang 发出警告,所以你应该为它们使用immintrin.h。其他编译器我不清楚。) - Peter Cordes
显示剩余7条评论

93

在GCC / clang上,如果你只使用

#include <x86intrin.h>

这个头文件将包括所有根据编译器开关(如-march=haswell-march=native)启用的SSE/AVX头文件。此外,一些特定于x86的指令,如bswapror,会作为内建函数可用。


MSVC等效的头文件是<intrin.h>


如果你只想要可移植的SIMD,请使用#include <immintrin.h>

MSVC、ICC以及gcc/clang(还有其他编译器,例如Sun)都支持该头文件,用于Intel唯一Intrinsics查找工具/搜索工具记录的SIMD内建函数:https://software.intel.com/sites/landingpage/IntrinsicsGuide/


我不确定较新的版本是否支持,但只要gcc、icc和clang有它,就可以使用了,我认为没问题 :-) - Gunther Piez
6
MSVC没有<x86intrin.h>头文件,但是<intrin.h>可以实现类似的效果。当然,你仍然需要条件编译。 :-( - Cody Gray
1
所有主要的x86编译器都有**#include <immintrin.h>**。使用它来进行SIMD内嵌函数。只有当您需要像整数旋转/位扫描内嵌函数这样的东西时,您才需要更大(并且稍微慢一些)的x86intrin.hintrin.h(尽管英特尔在其内嵌函数指南中将其中一些文档化为在immintrin.h中可用)。 - Peter Cordes
据我所知,英特尔文档中指定在immintrin.h中的一些非SIMD内在函数,在gcc、clang和/或MSVC中只在x86intrin.h / intrin.h中提供,而不在immintrin.h中。 - Peter Cordes

65

头文件的名称取决于你的编译器和目标架构。

  • 对于 Microsoft C++ (目标架构为 x86、x86-64 或 ARM) 和 Intel C/C++ Compiler for Windows,请使用 intrin.h
  • 对于 gcc/clang/icc 目标架构为 x86/x86-64,请使用 x86intrin.h
  • 对于 gcc/clang/armcc 目标架构为带 NEON 的 ARM,请使用 arm_neon.h
  • 对于 gcc/clang/armcc 目标架构为带 WMMX 的 ARM,请使用 mmintrin.h
  • 对于 gcc/clang/xlcc 目标架构为带 VMX(也称为 Altivec)和/或 VSX 的 PowerPC,请使用 altivec.h
  • 对于 gcc/clang 目标架构为带 SPE 的 PowerPC,请使用 spe.h

你可以使用条件预处理指令来处理所有这些情况:

#if defined(_MSC_VER)
     /* Microsoft C/C++-compatible compiler */
     #include <intrin.h>
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
     /* GCC-compatible compiler, targeting x86/x86-64 */
     #include <x86intrin.h>
#elif defined(__GNUC__) && defined(__ARM_NEON__)
     /* GCC-compatible compiler, targeting ARM with NEON */
     #include <arm_neon.h>
#elif defined(__GNUC__) && defined(__IWMMXT__)
     /* GCC-compatible compiler, targeting ARM with WMMX */
     #include <mmintrin.h>
#elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__))
     /* XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX */
     #include <altivec.h>
#elif defined(__GNUC__) && defined(__SPE__)
     /* GCC-compatible compiler, targeting PowerPC with SPE */
     #include <spe.h>
#endif

这里还有一些可以添加到您的列表中:在使用gcc的UltraSPARC+VIS上,请使用visintrin.h;如果您有Sun的VSDK,则vis.h提供了不同的内部函数集。文档可以在此处找到:GCC VIS内置函数Sun VIS用户指南 - onitake

53

从这个页面开始

+----------------+------------------------------------------------------------------------------------------+
|     Header     |                                         Purpose                                          |
+----------------+------------------------------------------------------------------------------------------+
| x86intrin.h    | Everything, including non-vector x86 instructions like _rdtsc().                         |
| mmintrin.h     | MMX (Pentium MMX!)                                                                       |
| mm3dnow.h      | 3dnow! (K6-2) (deprecated)                                                               |
| xmmintrin.h    | SSE + MMX (Pentium 3, Athlon XP)                                                         |
| emmintrin.h    | SSE2 + SSE + MMX (Pentium 4, Athlon 64)                                                  |
| pmmintrin.h    | SSE3 + SSE2 + SSE + MMX (Pentium 4 Prescott, Athlon 64 San Diego)                        |
| tmmintrin.h    | SSSE3 + SSE3 + SSE2 + SSE + MMX (Core 2, Bulldozer)                                      |
| popcntintrin.h | POPCNT (Nehalem (Core i7), Phenom)                                                       |
| ammintrin.h    | SSE4A + SSE3 + SSE2 + SSE + MMX (AMD-only, starting with Phenom)                         |
| smmintrin.h    | SSE4_1 + SSSE3 + SSE3 + SSE2 + SSE + MMX (Penryn, Bulldozer)                             |
| nmmintrin.h    | SSE4_2 + SSE4_1 + SSSE3 + SSE3 + SSE2 + SSE + MMX (Nehalem (aka Core i7), Bulldozer)     |
| wmmintrin.h    | AES (Core i7 Westmere, Bulldozer)                                                        |
| immintrin.h    | AVX, AVX2, AVX512, all SSE+MMX (except SSE4A and XOP), popcnt, BMI/BMI2, FMA             |
+----------------+------------------------------------------------------------------------------------------+

一般而言,您只需包含immintrin.h即可获得所有英特尔扩展,或者如果您需要包括_bit_scan_forward_rdtsc以及包括AMD专有的所有矢量内在函数,则使用x86intrin.h。如果您反对包含实际不需要的内容,则可以通过查看表格选择正确的包含文件。

x86intrin.h是获取 AMD XOP(仅适用于Bulldozer,甚至未来的AMD CPU)内在函数的推荐方法,而不是具有自己的头文件。

一些编译器仍然会生成错误消息,如果您使用尚未启用的指令集的内在函数(例如,如果您包括immintrin.h并启用了AVX2,则没有启用fma的情况下使用_mm_fmadd_ps)。


1
smmintrin(SSE4.1)是Penryn(45nm Core2),而不是Nehalem(“i7”)。我们能否停止使用“i7”作为架构名称?现在英特尔已经继续在SnB系列中使用它,这意义已经不存在了 - Peter Cordes
immintrin.h 在 GCC 9.1.0 中似乎没有包含 _popcnt32_popcnt64(不要与 popcntintrin.h 中的那些混淆!)内置函数。因此,看来 x86intrin.h 仍然有其作用。 - Thom Wiggers

24

20200914: 最新最佳实践: <immintrin.h>(也受到MSVC支持)。

我将保留其余的回答以供历史记录使用;它可能对旧的编译器/平台组合很有用...


正如许多答案和评论所述,<x86intrin.h>是x86[-64] SIMD内部函数的综合头文件。它还提供支持其他ISA扩展指令的内部函数。 gccclangicc都已经在此上达成共识。我需要挖掘一些支持头文件的版本,并认为列出一些发现可能会很有用...

  • gccx86intrin.h支持首次出现在gcc-4.5.0。 不再维护gcc-4发布系列,而gcc-6.x当前稳定发布系列。 gcc-5还引入了所有clang-3.x版本中存在的__has_include扩展。 gcc-7处于预发布状态(回归测试等),根据当前的版本控制方案,将发布为gcc-7.1.0

  • clang:所有clang-3.x版本似乎都支持x86intrin.h。 最新的稳定发行版是clang(LLVM)3.9.1。 开发分支是clang(LLVM)5.0.0。 不清楚4.x系列发生了什么。

  • Apple clang:恼人的是,Apple的版本号不对应LLVM项目的版本号。 话虽如此,当前版本:clang-800.0.42.1基于LLVM 3.9.0。第一个基于LLVM 3.0的版本似乎是Xcode 4.1中的Apple clang 2.1LLVM 3.1Xcode 4.3.3中首次出现,并与Apple clang 3.1(数字上的巧合)一起出现。

    Apple还定义了__apple_build_version__例如8000042。 这似乎是可用的最稳定、严格上升的版本控制方案。 如果您不想支持旧编译器,将其中一个值作为最低要求即可。

因此,任何最新版本的clang,包括Apple版本,都不应该有x86intrin.h的问题。 当然,除了gcc-5外,您始终可以使用以下内容:

#if defined (__has_include) && (__has_include(<x86intrin.h>))
#include <x86intrin.h>
#else
#error "upgrade your compiler. it's free..."
#endif

有一个技巧你不能太依赖,那就是在clang中使用__GNUC__的版本。由于历史原因,版本控制被固定在了4.2.1上,在x86intrin.h头文件之前。但对于一些简单的保持向后兼容性的GNU C扩展来说偶尔还是很有用的。

  • icc: 据我所知,自至少Intel C++ 16.0以来,支持x86intrin.h头文件。版本测试可以使用:#if (__INTEL_COMPILER >= 1600)。 这个版本(可能还包括更早的版本)还提供了__has_include扩展的支持。

  • MSVC: 看起来MSVC++ 12.0 (Visual Studio 2013)是第一个提供intrin.h头文件而非x86intrin.h的版本……这表明:版本测试应该是#if (_MSC_VER >= 1800)。当然,如果你试图编写能够跨所有这些不同编译器的可移植代码,此平台上的头文件名将是你最不关心的问题。


1
我更喜欢使用__has_builtin而不是烦人的版本检查。同时需要注意,GCC在某些内建函数上仍存在一些错误;在这种情况下,我会考虑使用特定于目标的内建函数,即使它们是未经记录的 - FrankHB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接