LLVM 项目博客

LLVM 项目新闻和来自底层的详细信息

LLVM 3.3 向量化改进

我想简要介绍一下 LLVM 中向量化的最新进展。LLVM 3.2 发布时,它包含了一个新的实验性循环向量化器,默认情况下是禁用的。自 LLVM 3.2 发布以来,我们一直在努力改进向量化,现在有一些新消息要分享。首先,循环向量化器具有新功能,现在在 -O3 级别默认启用。其次,我们有一个新的 SLP 向量化器。最后,我们有新的 Clang 命令行参数来控制向量化器。

循环向量化器

LLVM 循环向量化器有许多新功能,使它能够向量化更复杂的循环并获得更好的性能。我们关注的一个领域是向量化“成本模型”。当 LLVM 评估循环是否能从向量化中获益时,它使用对处理器的详细描述来估计各种指令的成本。我们改进了 X86 和 ARM 成本模型。改进成本模型有助于编译器检测出受益循环,并提高许多程序的性能。在分析向量化程序期间,我们也发现了并优化了许多向量代码序列。

对循环向量化器的另一个重要改进是,它能够在向量化期间展开循环。当编译器展开循环时,它会生成更多独立的指令,现代乱序处理器可以并行执行这些指令。下面的循环将数组中的所有数字加起来。在编译这个循环时,LLVM 会创建两个独立的计算链,可以并行执行。

int sum_elements(int *A, int n) {
int sum = 0;
for (int i = 0; i < n; ++i)
sum += A[i];
return sum;
}
上面程序的最内层循环被编译成下面的 X86 汇编序列,它一次处理 8 个元素,在两个并行的计算链中执行。向量寄存器 XMM0 和 XMM1 用于存储数组不同部分的局部和。这使得处理器能够同时加载两个值并添加两个值。

LBB0_4:
movdqu 16(%rdi,%rax,4), %xmm2
paddd %xmm2, %xmm1
movdqu (%rdi,%rax,4), %xmm2
paddd %xmm2, %xmm0
addq $8, %rax
cmpq %rax, %rcx
jne LBB0_4

另一个重要改进是支持包含 IF 语句的循环,以及检测流行的最小/最大模式。LLVM 现在能够向量化下面的代码

int fins_max(int *A, int n) {
int mx = A[0];
for (int i = 0; i < n; ++i)
if (mx > A[i])
mx = A[i];
return mx;
}
在上一版本中,循环向量化器能够向量化许多包含浮点运算的循环,但并非全部。由于独特的舍入规则,浮点运算不是结合的。这意味着表达式 (a + b) + c 不总是等于 a + (b + c)。编译器标志 -ffast-math 告诉编译器不要担心舍入误差,而是为了速度进行优化。循环向量化器的新功能之一是在使用 -ffast-math 模式时向量化浮点运算。决定使用 -ffast-math 标志的用户会注意到,使用 LLVM 的即将发布的 3.3 版本,更多的循环将被向量化。

SLP 向量化器

SLP 向量化器(简称超字级并行)是一个新的向量化过程。与循环向量化器不同,循环向量化器向量化连续的循环迭代,SLP 向量化器将直线代码中的类似独立指令组合在一起。
SLP 向量化器现在可用,并将对许多人有用。
SLP 向量化器可以提高 LLVM 测试套件中许多程序的性能。在一个基准测试“Olden/Power”中,SLP 向量化器将程序的性能提升了 16%。以下是一个 SLP 向量化器可以向量化的函数的小示例。

void foo(int * restrict A, int * restrict B) {
A[0] = 7+(B[0] * 11);
A[1] = 6+(B[1] * 12);
A[2] = 5+(B[2] * 13);
A[3] = 4+(B[3] * 14);
}
上面的代码被编译成下面的 ARMv7s 汇编序列。请注意,4 个加法和 4 个乘法运算变成了一个单一的乘加指令 "vmla"。

_foo:
adr r2, LCPI0_0
adr r3, LCPI0_1
vld1.32 {d18, d19}, [r1]
vld1.64 {d16, d17}, [r3:128]
vld1.64 {d20, d21}, [r2:128]
vmla.i32 q10, q9, q8
vst1.32 {d20, d21}, [r0]
bx lr

命令行标志

我们还为 Clang 添加了新的命令行标志来控制向量化器。循环向量化器在 -O3 级别默认启用,可以使用命令行标志在其他优化级别启用或禁用它

$ clang ... -fvectorize / -fno-vectorize file.c
SLP 向量化器默认禁用,可以使用命令行标志启用它

$ clang ... -fslp-vectorize file.c
LLVM 有第二个更耗费编译时间的基本块向量化阶段(BB 向量化器)。可以使用命令行标志通过 Clang 启用此优化

$ clang ... -fslp-vectorize-aggressive file.c
在 LLVM 3.3 的开发过程中,我们在改进向量化方面取得了巨大进展。特别感谢所有为这项工作做出贡献的人。