为多个 SIMD 架构生成代码

Generate code for multiple SIMD architectures

我编写了一个库,我在其中使用 CMake 验证 MMX、SSE、SSE2、SSE4、AVX、AVX2 和 AVX-512 的 headers 是否存在。除此之外,我检查指令是否存在,如果存在,我添加必要的编译器标志,-msse2 -mavx -mfma 等。

一切都很好,但我想部署一个二进制文件,它适用于多代处理器。

问题:是否可以告诉编译器 (GCC) 每当它使用 SIMD 优化函数时,它必须为一系列体系结构生成代码?当然还有 high-level 个分支

我的想法类似于编译器生成函数代码的方式,其中输入指针是 4 字节或 8 字节对齐的。为了防止这种情况,我使用 __builtin_assume_aligned 宏。

什么是最佳做法?多个二进制文件?命名?

只要不在乎便携性就可以。

最新版本的 GCC 通过使用 target_clones 函数属性使这比我所知道的任何其他编译器都更容易。只需添加属性,以及您要为其创建版本的目标列表,GCC 将自动创建不同的变体,以及在运行时自动选择版本的调度功能。

如果你想要更多的可移植性,你可以使用 target 属性,clang 和 icc 也支持,但是你必须自己编写调度函数(这并不难),并且多次发出函数(通常使用宏,或重复包含 header)。

A​​FAIK,如果您希望您的代码与 MSVC 一起工作,您将需要使用不同选项调用多个编译器。

如果你只是在谈论让编译器生成 SSE/AVX 等指令,并且你有 "general purpose" 代码(即你没有使用内在函数显式矢量化,或者 lots 编译器将发现并自动矢量化的代码)然后我应该警告你,编译整个代码库的 AVX、AVX2 或 AVX512 可能 运行 显着 比为 SSE 版本编译更慢

当检测到使用寄存器上半部分的 AVX 操作码时,CPU 会为电路的上半部分加电(否则会断电)。这会消耗更多功率,产生更多热量并降低芯片的基本时钟速度,通常会降低 10-20%,具体取决于高功率和低功率操作码的组合,因此您可能会立即损失 15% 的性能,然后在开始看到任何收益之前,要进行大量矢量化处理以弥补这种性能缺陷。

this thread 中查看我更长的解释和参考资料。

另一方面,如果您明确地使用内在函数进行矢量化,并且您确定您有足够大的 AVX 等爆发使其值得,我已经成功地编写了代码,我告诉 MSVC 为 SSE2 编译( x64 的默认值),但随后我动态检查 CPU 功能,并且一些函数切换到使用 AVX 内在函数实现的代码路径。

MSVC 允许这样做(它会产生警告,但您可以将它们静音),但是相同的技术很难在 GCC 4.9 下工作,因为只有在适当的代码生成标志为用过的。 [更新:@nemequ 在下面解释了如何在 gcc 下使用属性来装饰函数] 根据 GCC 的版本,您可能必须使用不同的标志编译文件以获得可行的系统。

哦,你还必须注意 AVX-SSE 转换(当你将 AVX 代码部分留给 return 到 SSE 代码时调用 VZEROUPPER)——可以做到,但我发现理解CPU 影响是一场比我最初设想的更大的战斗。