哪些头文件为不同的 x86 SIMD 指令集扩展(MMX、SSE、AVX...)提供了内在函数?在网上似乎不可能找到这样的清单。如我错了请纠正我。
5 回答
这些天你通常应该只包括<immintrin.h>
. 它包括一切。
GCC 和 clang 将阻止您使用内在函数来执行您在编译时未启用的指令(例如,使用-march=native
or-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
不拉入)
一些编译器也有<zmmintrin.h>
AVX512。
在 GCC/clang 上,如果你只使用
#include <x86intrin.h>
它将包括根据编译器开关启用的所有 SSE/AVX 标头,-march=haswell
例如-march=native
. 此外,一些 x86 特定指令类似于bswap
或ror
可用作内在函数。
此标头的 MSVC 等效项<intrin.h>
如果您只想要便携式 SIMD,请使用#include <immintrin.h>
MSVC、ICC 和 gcc/clang(以及我认为的其他编译器,如 Sun)都支持英特尔唯一的内部函数查找器/搜索工具记录的 SIMD 内部函数的此标头:https ://software.intel.com/sites/landingpage/IntrinsicsGuide /
标头名称取决于您的编译器和目标体系结构。
- 对于 Microsoft C++(面向 x86、x86-64 或 ARM)和适用于 Windows 的 Intel C/C++ 编译器
intrin.h
- 对于针对 x86/x86-64 的 gcc/clang/icc 使用
x86intrin.h
- 对于使用 NEON 以 ARM 为目标的 gcc/clang/armcc
arm_neon.h
- 对于 gcc/clang/armcc,使用 WMMX 以 ARM 为目标
mmintrin.h
- 对于带有 VMX(又名 Altivec)和/或 VSX 的针对 PowerPC 的 gcc/clang/xlcc 使用
altivec.h
- 对于使用 SPE 的 gcc/clang 目标 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
从这个页面
+----------------+------------------------------------------------------------------------------------------+
| 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
and在内的所有内容,_rdtsc
以及所有矢量内在函数都包括仅 AMD 的。如果您反对包含更多您实际需要的内容,那么您可以通过查看表格来选择正确的包含。
x86intrin.h
是获得AMD XOP 内在函数的推荐方法(仅限推土机,甚至未来的 AMD CPU),而不是拥有自己的标头。
_mm_fmadd_ps
如果您对未启用的指令集使用内部函数(例如,即使您包含immintrin.h
并启用了 AVX2 ,也未启用 fma),某些编译器仍会生成错误消息。
20200914:最新的最佳实践:(<immintrin.h>
也受MSVC支持)
我将把剩下的答案留作历史用途;它可能对较旧的编译器/平台组合有用......
正如许多答案和评论所述,<x86intrin.h>
是x86[-64] SIMD 内在函数的综合标头。它还为其他 ISA 扩展提供内在函数支持指令。gcc
, clang
, 并且icc
都解决了这个问题。我需要对支持标头的版本进行一些挖掘,并认为列出一些发现可能很有用......
gcc : 支持
x86intrin.h
first 出现在gcc-4.5.0
.gcc-4
发布系列不再维护,gcc-6.x
而是当前稳定的发布系列。gcc-5
还介绍了所有版本中__has_include
存在的扩展。处于预发布(回归测试等)并遵循当前版本控制方案,将发布为.clang-3.x
gcc-7
gcc-7.1.0
clang:
x86intrin.h
似乎所有clang-3.x
版本都支持。最新的稳定版本是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
首先出现在Apple clang 3.1
(数字巧合)中Xcode 4.3.3
。
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
您不能真正依赖的一个技巧是__GNUC__
使用clang
. 由于历史原因,版本控制一直停留在4.2.1
. x86intrin.h
标题之前的版本。它有时对保持向后兼容的简单 GNU C 扩展很有用。
icc:据我所知,
x86intrin.h
至少从 Intel C++ 16.0 开始就支持标头。版本测试可以通过以下方式执行:#if (__INTEL_COMPILER >= 1600)
。此版本(可能还有更早的版本)还提供对__has_include
扩展的支持。MSVC:这似乎
MSVC++ 12.0 (Visual Studio 2013)
是第一个提供intrin.h
标头的版本 -不是x86intrin.h
......这表明:#if (_MSC_VER >= 1800)
作为版本测试。当然,如果您尝试编写可在所有这些不同编译器之间移植的代码,那么此平台上的标头名称将是您遇到的最少问题。