gcc
编译器的v4系列可以自动使用一些现代CPU(如AMD Athlon或Intel Pentium/Core芯片)上的SIMD处理器对循环进行矢量化。这是如何实现的呢?
gcc
编译器的v4系列可以自动使用一些现代CPU(如AMD Athlon或Intel Pentium/Core芯片)上的SIMD处理器对循环进行矢量化。这是如何实现的呢?
这个页面详细介绍了如何让gcc自动向量化循环,其中包括一些示例:
http://gcc.gnu.org/projects/tree-ssa/vectorization.html
尽管这些示例很好,但是最新的GCC调用这些选项的语法似乎有些变化,请参见以下链接:
总之,以下选项适用于具有SSE2的x86芯片,可以记录已向量化的循环日志:
gcc -O2 -ftree-vectorize -msse2 -mfpmath=sse -ftree-vectorizer-verbose=5
请注意,-msse也是一个选项,但它只能使用浮点数来矢量化循环,不能使用双精度或整数。(对于x86-64,SSE2是基线。对于32位代码,请同时使用-mfpmath=sse。这是64位的默认值,但不是32位的默认值。)gcc -O3 -msse2 -mfpmath=sse -ftree-vectorizer-verbose=5
(Clang启用自动向量化的优化级别为-O2
。ICC默认启用优化和快速数学计算。)
以下大部分内容由Peter Cordes编写,他本可以写一个新答案。随着编译器变化,选项和编译器输出将会改变。我不确定是否值得在这里详细追踪。有评论吗?--作者
要同时使用硬件支持的指令集扩展并进行针对性优化,请使用-march=native
。
归约循环(例如数组总和)需要OpenMP或-ffast-math
将FP数学视为可结合的并进行矢量化。在Godbolt编译器浏览器上使用-O3 -march=native -ffast-math
的示例包括一个归约(数组求和),如果没有-ffast-math
则为标量。(好吧,GCC8及更高版本会执行SIMD加载,然后解压缩为标量元素,这与简单展开无意义。循环瓶颈在于一个addss
依赖链的延迟。)
有时候您并不需要-ffast-math
,只需要-fno-math-errno
就可以帮助gcc内联数学函数并对涉及sqrt
和/或rint
/nearbyint
的矢量化进行优化。
其他有用的选项包括-flto
(链接时优化,用于交叉文件内联、常量传播等)和/或使用-fprofile-generate
/测试运行(s)进行基于性能分析的优化/-fprofile-use
。PGO使“热”循环展开; 在现代GCC中,即使在-O3级别下也默认关闭此功能。
-O3
级别下启用自动向量化,请优先选择该选项。现在默认情况下不会启用循环展开。理想情况下,可以使用-fprofile-generate
和-fprofile-use
来让编译器自动展开热点循环。当仅为您的计算机编译时,请优先选择-O3 -march=native -ffast-math
选项。此外,参见 C loop optimization help for final assignment 以获取一些GCC自动向量化和自动并行化的示例,这些示例适用于较新版本的GCC。 - Peter Cordes有一个GIMPLE(GCC的中间表示)通道pass_vectorize
。此通道将在gimple级别启用自动向量化。
要启用自动向量化(GCC V4.4.0),需要执行以下步骤:
UNITS_PER_SIMD_WORD
来完成此操作。<target>-modes.def
。此文件必须位于其他包含机器描述文件的目录中。(根据配置脚本。如果您可以更改脚本,则可以将文件放置在任何目录中)。按目标架构考虑进行矢量化的模式。例如:4个单词将构成一个向量或8个半字将构成一个向量或2个双字将构成一个向量。这些细节需要在<target>-modes.def
文件中说明。例如:
VECTOR_MODES (INT, 8); /* V8QI V4HI V2SI /
VECTOR_MODES (INT, 16); / V16QI V8HI V4SI V2DI /
VECTOR_MODES (FLOAT, 8); / V4HF V2SF */
构建端口。可以使用命令行选项-O2 -ftree-vectorize
启用矢量化。