使用 SSE / AVX Intrinisics 时架构的影响

Roy*_*oyi 1 gcc sse icc intrinsics avx

我想知道编译器如何处理内部函数。

如果使用 SSE2 Intrinsics (Using #include <emmintrin.h>) 并使用-mavx标志进行编译。编译器会生成什么?它会生成 AVX 或 SSE 代码吗?

如果使用 AVX2 Intrinsics (Using #include <immintrin.h>) 并使用-msse2标志进行编译。编译器会生成什么?它会生成 SSE Only 或 AVX 代码吗?

编译器如何处理内部函数?
如果使用 Intrinsics,它是否可以帮助编译器理解循环中的依赖关系以实现更好的矢量化?

例如,这里发生了什么 - https://godbolt.org/z/Y4J5OA(或https://godbolt.org/z/LZOJ2K)?
查看全部 3 个窗格。

上下文

我正在尝试构建具有不同 CPU 功能(SSE4 和 AVX2)的相同功能的各种版本。
我正在使用 SSE Intrinsics 编写相同的版本,并且使用 AVX Intrinsics 编写一次。
假设他们的名字MyFunSSE()是 和MyFunAVX()。两者都在同一个文件中。

如何让编译器(相同的方法适用于 MSVC、GCC 和 ICC)仅使用各自的函数来构建它们?

Pet*_*des 5

GCC 和 clang 要求您启用您使用的所有扩展。否则,这是一个编译时错误,例如error: inlined failed to call always_inline error: inlining failed in call to always_inline \xe2\x80\x98__m256d _mm256_mask_loadu_pd(__m256d, __mmask8, const void*)\xe2\x80\x99: target specific option mismatch

\n

与启用特定扩展相比,使用-march=nativeor-march=haswell或 任何内容都更好,因为这也设置了适当的调整选项。并且您不会忘记类似的有用指令,-mpopcnt可以让指令std::bitset::count()内联popcnt,并使所有变量计数移位通过 BMI2 shlx/ shrx(1 uop vs. 3)变得更加高效

\n
\n

MSVC 和 ICC 没有,并且允许您使用内在函数来发出它们无法自动矢量化的指令。

\n

如果您使用 AVX 内在函数,那么您绝对应该启用 AVX。未启用 AVX 的旧版 MSVC 并不总是vzeroupper在需要时自动使用,但这已经修复了几年。不过,如果您的整个程序可以假定 AVX 支持,请务必告诉编译器,即使对于 MSVC 也是如此。

\n
\n

对于支持 GNU 扩展(GCC、clang、ICC)的编译器,您可以__attribute__((target("avx")))在编译单元中的特定函数上使用类似的内容。或者更好的是,__attribute__((target("arch=haswell")))还可以设置调整选项。(这还启用了 AVX2 和 FMA,您可能不想要这些。而且我不确定target属性是否已设置-mtune=xx)。请参阅\n https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html

\n

__attribute__((target()))将阻止它们内联到具有其他目标选项的函数中,因此如果函数本身太小,请小心地在它们将内联到的函数上使用此选项。将其用于包含循环的函数,而不是在循环中调用的辅助函数。

\n

另请参阅\n https://gcc.gnu.org/wiki/FunctionMultiVersioning,了解在同一函数名称的多个定义上使用不同的目标选项,以实现编译器支持的运行时调度。但我认为没有一种可移植的(MSVC)方法可以做到这一点。

\n

有关在 GCC/clang 上执行运行时调度的更多信息,请参阅指定编译器可以使用的函数的 simd 级别。

\n
\n

使用 MSVC 你不需要任何东西,尽管就像我说的,我认为使用 AVX 内在函数而不使用 AVX 内在函数通常是一个坏主意-arch:AVX,所以你最好将它们放在一个单独的文件中。但对于 AVX 与 AVX2 + FMA,或 SSE2 与 SSE4.2,你什么都不用就可以了。

\n

只是#define AVX2_FUNCTION空字符串而不是__attribute__((target("avx2,fma")))

\n
#if defined(__GNUC__) && !defined(__INTEL_COMPILER)\n// apparently ICC doesn\'t support target attributes, despite supporting GNU C\n#define TARGET_HASWELL __attribute__((target("arch=haswell")))\n#else\n#define TARGET_HASWELL   // empty\n // maybe warn if __AVX__ isn\'t defined for functions where this is used?\n // if you need to make sure MSVC uses vzeroupper everywhere needed.\n#endif\n\n\nTARGET_HASWELL\nvoid foo_avx(float *__restrict dst, float *__restrict src)\n{\n   for (size_t i = 0 ; i<1024 ; i++) {\n       __m256 v = _mm256_loadu_ps(src);\n       ...\n       ...\n   }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

有了 GCC 和 clang,宏就扩展到了__attribute__((target))东西;对于 MSVC 和 ICC 则不然。

\n
\n

ICC 指令:

\n

https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-optimization-parameter记录了您想要放在 AVX 函数之前的编译指示,以确保正确使用 vzeroupper在使用_mm256内部函数的函数中。

\n
#pragma intel optimization_parameter target_arch=AVX\n
Run Code Online (Sandbox Code Playgroud)\n

对于 ICC,您可以#define TARGET_AVX这样,并且始终在函数之前单独使用它,您可以在其中放置__attribute__或 编译指示。如果 ICC 不希望在声明中使用此宏,您可能还需要单独的宏来定义和声明函数。如果您想在 AVX 函数之后添加非 AVX 函数,则可以使用宏来结束 AVX 函数块。(对于非 ICC 编译器,该字段将为空。)

\n