【发布时间】:2012-06-29 00:59:28
【问题描述】:
哪些头文件为不同的 x86 SIMD 指令集扩展(MMX、SSE、AVX、...)提供了内在函数?在网上似乎不可能找到这样的清单。如果我错了,请纠正我。
【问题讨论】:
标签: x86 header-files sse simd intrinsics
哪些头文件为不同的 x86 SIMD 指令集扩展(MMX、SSE、AVX、...)提供了内在函数?在网上似乎不可能找到这样的清单。如果我错了,请纠正我。
【问题讨论】:
标签: x86 header-files sse simd intrinsics
这些天你通常应该只包括<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>。
【讨论】:
#include <x86intrin.h> 获取你需要的一切。
<zmmintrin.h>; gcc 甚至不提供它。 只需使用 <immintrin.h> 或更完整的 <x86intrin.h>。这个答案基本上已经过时了,除非您有意避免包含较新版本的 SSE 的内在函数,因为在为 SSE2 编译时使用 SSE4.1 指令时编译器不会抱怨。 (gcc/clang do 抱怨,所以你应该只为他们使用 immintrin.h。关于其他人的 IDK。)
在 GCC/clang 上,如果你只使用
#include <x86intrin.h>
它将包括所有根据编译器开关启用的 SSE/AVX 标头,例如 -march=haswell 或只是 -march=native。此外,一些 x86 特定指令(如 bswap 或 ror)可作为内部函数使用。
此标头 <intrin.h> 的 MSVC 等效项
如果您只需要便携式 SIMD,请使用 #include <immintrin.h>
MSVC、ICC 和 gcc/clang(以及我认为的其他编译器,如 Sun)都支持英特尔唯一的内部函数查找器/搜索工具记录的 SIMD 内部函数的此标头:https://software.intel.com/sites/landingpage/IntrinsicsGuide/
【讨论】:
<x86intrin.h>,但<intrin.h> 实现了类似的效果。当然,您仍然需要条件编译。 :-(
#include <immintrin.h>。将其用于 SIMD 内在函数。如果您需要整数旋转/位扫描内在函数之类的东西,您只需要更大(并且编译器稍微慢一些)x86intrin.h 或 intrin.h(尽管英特尔记录了其中一些在 immintrin.h in their intrinsics guide 中可用)。
x86intrin.h / intrin.h 中具有,但 不在immintrin.h中。
标头名称取决于您的编译器和目标架构。
intrin.h
x86intrin.h
arm_neon.h
mmintrin.h
altivec.h
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
【讨论】:
来自page
+----------------+------------------------------------------------------------------------------------------+
| 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 以获取所有 Intel 扩展,或包含 x86intrin.h 如果您想要所有内容,包括 _bit_scan_forward 和 _rdtsc,以及所有矢量内在函数都包括 AMD 专用的。如果您反对包含更多您实际需要的内容,那么您可以通过查看表格来选择正确的包含。
x86intrin.h 是获取AMD XOP (Bulldozer-only, not even future AMD CPUs) 的内在函数的推荐方法,而不是拥有自己的标头。
如果您对未启用的指令集使用内部函数(例如 _mm_fmadd_ps 而不启用 fma,即使您包含 immintrin.h 并启用 AVX2),某些编译器仍会生成错误消息。
【讨论】:
smmintrin (SSE4.1) 是 Penryn (45nm Core2),而不是 Nehalem ("i7")。我们可以停止使用“i7”作为架构名称吗? It's meaningless now that Intel has kept using it for SnB-family.
immintrin.h 似乎不包括 _popcnt32 和 _popcnt64(不要与 popcntintrin.h 中的那些混淆!)GCC 9.1.0 的内在函数。所以看来x86intrin.h 仍然是有目的的。
20200914: 最新最佳实践:<immintrin.h>(也受 MSVC 支持)
我将把剩下的答案留作历史用途;它可能对较旧的编译器/平台组合有用...
正如许多答案和 cmets 所述,<x86intrin.h> 是 x86[-64] SIMD 内在函数的综合标头。它还为其他 ISA 扩展提供内在函数支持指令。 gcc、clang 和 icc 都解决了这个问题。我需要对支持标头的版本进行一些挖掘,并认为列出一些发现可能很有用...
gcc :对x86intrin.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 的版本似乎是Apple clang 2.1 回到Xcode 4.1。 LLVM 3.1 在Xcode 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)作为版本测试。当然,如果您尝试编写可在所有这些不同编译器之间移植的代码,那么这个平台上的标头名称将是您最少的问题。
【讨论】:
__has_builtin 而不是烦人的版本检查。另请注意当前GCC still has some bugs on specific buitins;在这种情况下,我会考虑针对特定目标的even undocumented。