我有以下代码,它似乎是SLP的完美候选者:
struct vector {
double x, y, z;
} __attribute__((aligned(16)));
int
slp_test(struct vector *x0, struct vector *n)
{
double t = -x0->z/n->z;
double u = x0->x + t*n->x;
double v = x0->y + t*n->y;
return t >= 0.0 && u >= 0.0 && v >= 0.0 && u + v <= 1.0;
}
u
和v
的计算似乎很容易实现矢量化,而x0
和n
应该足够好地对齐它。但是在-O3
的x86-64上,gcc 4.9.0生成了这段代码:
movsd .LC0(%rip), %xmm1
movsd 16(%rdi), %xmm0
movsd (%rdi), %xmm2
xorpd %xmm1, %xmm0
movsd (%rsi), %xmm1
pxor %xmm3, %xmm3
divsd 16(%rsi), %xmm0 ; t = x0->z/n->z
mulsd %xmm0, %xmm1 ; t*n->x
addsd %xmm1, %xmm2 ; u = x0->x + t*n->x
movsd 8(%rsi), %xmm1
mulsd %xmm0, %xmm1 ; t*n->y
ucomisd %xmm3, %xmm2
addsd 8(%rdi), %xmm1 ; v = x0->y + t*n->y
setae %dl
ucomisd %xmm3, %xmm1
setae %al
testb %al, %dl
je .L3
ucomisd %xmm3, %xmm0
jb .L3
addsd %xmm2, %xmm1
movsd .LC2(%rip), %xmm0
xorl %eax, %eax
ucomisd %xmm1, %xmm0
setae %al
ret
.L3:
xorl %eax, %eax
ret
为什么gcc不使用mulpd
addpd
代替两个mulsd
和addsd
?我使用-fopt-info-all-vec
试图了解原因,并抱怨对齐(full output):
slp-test.c:8:17: note: === vect_analyze_data_refs_alignment ===
slp-test.c:8:17: note: vect_compute_data_ref_alignment:
slp-test.c:8:17: note: misalign = 0 bytes of ref x0_3(D)->z
slp-test.c:8:17: note: vect_compute_data_ref_alignment:
slp-test.c:8:17: note: misalign = 0 bytes of ref n_6(D)->z
slp-test.c:8:17: note: vect_compute_data_ref_alignment:
slp-test.c:8:17: note: misalign = 0 bytes of ref x0_3(D)->x
slp-test.c:8:17: note: vect_compute_data_ref_alignment:
slp-test.c:8:17: note: misalign = 0 bytes of ref n_6(D)->x
slp-test.c:8:17: note: vect_compute_data_ref_alignment:
slp-test.c:8:17: note: misalign = 8 bytes of ref x0_3(D)->y
slp-test.c:8:17: note: vect_compute_data_ref_alignment:
slp-test.c:8:17: note: misalign = 8 bytes of ref n_6(D)->y
slp-test.c:8:17: note: === vect_analyze_slp ===
slp-test.c:8:17: note: Failed to SLP the basic block.
slp-test.c:8:17: note: not vectorized: failed to find SLP opportunities in basic block.
除非我误解了__attribute__((aligned(16)))
,否则它应该能够强制对齐这些访问。有什么想法吗?
答案 0 :(得分:1)
此代码不会从矢量化中获益太多,请记住cpus能够在一个周期内执行多个指令。 例如,Nehalem乘法/加法的延迟为4,倒数吞吐量为1,因此在理想情况下,它应该能够在四个周期内计算这些指令中的4个。这里至少应该有2个。 这已经意味着即使向量寄存器已经完美归档,您也无法通过使用压缩指令获得任何收益。
编辑:我没有意识到数据可以一次加载,因此设置成本可以忽略不计
为了填充它们,你可能已经需要一个高和低的mov指令,这将比你以后获得的少量打包指令花费更多。 (在Nehalem上,mov [hl] pd的延迟为~5,而movsd的延迟为2)
比较无法有利地进行矢量化,因为您需要将打包的比较解包回正常的寄存器,这是一个非常昂贵的操作。 编译器也不知道分支的概率,它必须假设第一次比较总是会使其余部分短路,所以并行做任何事情都是有害的。
编辑:使用sse4 ptest虽然可能有利可图
这里的瓶颈也可能是不可归类的部门。 您可能更好地尝试一次矢量化2个结构的操作,而不是尝试在一个结构中矢量化操作。