首页 > 解决方案 > 使用 SSE / AVX Intrinisics 时架构的影响

问题描述

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

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

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

编译器如何处理 Intrinsics?
如果使用 Intrinsics,它是否有助于编译器理解循环中的依赖关系以实现更好的向量化?

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

上下文

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

我怎样才能使编译器(相同的方法应该适用于 MSVC、GCC 和 ICC)只使用各自的函数来构建它们?

标签: gcccompilationsseavxicc

解决方案


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

使用-march=haswell或任何比启用特定扩展更可取的方法,因为这也设置了适当的调整选项。而且你不会忘记像-mpopcnt这样有用的指令会让std::bitset::count()内联指令,并使用 BMI2 / (1 uop vs. 3)popcnt使所有变量计数移位更有效shlxshrx


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

如果您使用 AVX 内在函数,则绝对应该启用 AVX。我想我已经阅读/看到,没有它,MSVC 不会总是vzeroupper在它应该使用的地方使用。


对于支持 GNU 扩展(GCC、clang、ICC)的编译器,您可以使用诸如__attribute__((target("avx")))编译单元中特定函数之类的东西。或者更好的是,__attribute__((target("arch=haswell")))还可以设置调整选项。(但这也启用了您可能不想要的 AVX2 和 FMA。我不确定target属性是否可以设置-mtune=xx

https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes(以及

__attribute__((target()))将阻止它们内联到具有其他目标选项的函数中,因此如果函数本身太小,请小心在它们将内联到的函数上使用 this。

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


使用 MSVC,您不需要任何东西,尽管就像我说的那样,我认为在没有 AVX 内部函数的情况下使用它通常是一个坏主意-arch:AVX,因此您最好将它们放在单独的文件中。但是对于 AVX 与 AVX2 + FMA,或 SSE2 与 SSE4.2,没有任何东西就可以了。

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

例如

#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
// apparently ICC doesn't support target attributes
#define TARGET_HASWELL __attribute__((target("arch=haswell")))
#else
#define TARGET_HASWELL   // empty
 // maybe warn if __AVX__ isn't defined for functions where this is used?
 // if you need to make sure MSVC uses vzeroupper everywhere needed.
#endif


TARGET_HASWELL
void foo_avx(float *__restrict dst, float *__restrict src) {
    __m256 v = _mm256_loadu_ps(src);
    ...
    ...
}

使用 GCC 和 clang,宏扩展为这些__attribute__((target))东西;对于 MSVC 和 ICC,它不会。


ICC 编译指示:

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

#pragma intel optimization_parameter target_arch=AVX

对于ICC,你可以#define TARGET_AVX这样,并且总是在函数之前单独使用它,你可以在其中放置一个__attribute__或一个pragma。如果 ICC 不希望在声明中这样做,您可能还需要单独的宏来定义函数和声明函数。如果你想在它们之后有非 AVX 函数,还有一个宏来结束一个 AVX 函数块。(对于非 ICC 编译器,这将为空。)


推荐阅读