static void iadd(int &R, Vector &A) {
unsigned sum = 0;
int a;
for (int i=0; i<A.vector_elements_16; i++) {
a = static_cast<int>(A.data_16[i]);
sum += a ;
}
R=static_cast<int>(sum);
}
Vector类:具有宽度为32的静态数组,类型为uint16_t。所以迭代次数是32。
答案 0 :(得分:3)
问题中没有足够的信息可以自信地回答。但我向你保证,LLVM和Clang将至少在树的顶部矢量化减少循环(我还没有检查旧版本的功能)。
第一个问题是矢量化真的取决于架构。我将在我的示例中使用x86-64和Haswell微体系结构(支持AVX2),因为您没有列出特定的体系结构。如果您指定,我很乐意更新我对其他架构的答案。
接下来的问题是你的描述听起来不像真正的减少循环。首先,如果阵列是 static ,那么我真的不知道这是什么 - 这是一个全球性的。但假设您的意思是长度为32的成员数组,那应该等同于以下(稍微简化)完整并编译的代码:
struct V {
static constexpr int length = 32;
unsigned short data[32];
};
int reduce(V &v) {
int sum = 0;
for (int i = 0; i < v.length; ++i)
sum += static_cast<int>(v.data[i]);
return sum;
}
虽然给出了这个代码,因为长度是一个常量,LLVM完全展开循环,这意味着没有循环向量化将发挥作用。遗憾的是,我们实际上为展开的循环生成了非常糟糕的代码--32次加载和32次加载。我们可以做得更好,更好。我已经提交了http://llvm.org/PR28090来跟踪修复此问题。
但这不是一个真正的减少循环因为它具有恒定的rip计数并且被展开。如果你真的有一个循环,如下面的代码:
struct V {
int length;
unsigned short *data;
};
int reduce(V &v) {
int sum = 0;
for (int i = 0; i < v.length; ++i)
sum += static_cast<int>(v.data[i]);
return sum;
}
然后LLVM实际上会很好地为Haswell进行矢量化。它将8个元素加载到向量中,将它们扩展为32位值,对它们求和。它还一次完成4个这样的向量以充分利用架构的带宽。看看这里的代码:
_Z6reduceR1V: # @_Z6reduceR1V
.cfi_startproc
# BB#0: # %entry
movslq (%rdi), %rcx
xorl %eax, %eax
testq %rcx, %rcx
jle .LBB0_11
# BB#1: # %for.body.lr.ph
movq 8(%rdi), %rdx
xorl %edi, %edi
movl $0, %eax
cmpl $31, %ecx
jbe .LBB0_10
# BB#2: # %min.iters.checked
xorl %edi, %edi
movq %rcx, %r9
movl $0, %eax
andq $-32, %r9
je .LBB0_10
# BB#3: # %vector.body.preheader
leaq -32(%r9), %rsi
shrq $5, %rsi
leal 1(%rsi), %r8d
andl $1, %r8d
xorl %eax, %eax
testq %rsi, %rsi
je .LBB0_4
# BB#5: # %vector.body.preheader.new
leaq -1(%r8), %rdi
subq %rsi, %rdi
vpxor %ymm0, %ymm0, %ymm0
xorl %eax, %eax
vpxor %ymm1, %ymm1, %ymm1
vpxor %ymm2, %ymm2, %ymm2
vpxor %ymm3, %ymm3, %ymm3
.p2align 4, 0x90
.LBB0_6: # %vector.body
# =>This Inner Loop Header: Depth=1
vpmovzxwd (%rdx,%rax,2), %ymm4 # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpmovzxwd 16(%rdx,%rax,2), %ymm5 # ymm5 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpmovzxwd 32(%rdx,%rax,2), %ymm6 # ymm6 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpmovzxwd 48(%rdx,%rax,2), %ymm7 # ymm7 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpaddd %ymm0, %ymm4, %ymm0
vpaddd %ymm1, %ymm5, %ymm1
vpaddd %ymm2, %ymm6, %ymm2
vpaddd %ymm3, %ymm7, %ymm3
vpmovzxwd 64(%rdx,%rax,2), %ymm4 # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpmovzxwd 80(%rdx,%rax,2), %ymm5 # ymm5 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpmovzxwd 96(%rdx,%rax,2), %ymm6 # ymm6 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpmovzxwd 112(%rdx,%rax,2), %ymm7 # ymm7 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
leaq 64(%rax), %rax
vpaddd %ymm0, %ymm4, %ymm0
vpaddd %ymm1, %ymm5, %ymm1
vpaddd %ymm2, %ymm6, %ymm2
vpaddd %ymm3, %ymm7, %ymm3
addq $2, %rdi
jne .LBB0_6
jmp .LBB0_7
.LBB0_4:
vpxor %ymm0, %ymm0, %ymm0
vpxor %ymm1, %ymm1, %ymm1
vpxor %ymm2, %ymm2, %ymm2
vpxor %ymm3, %ymm3, %ymm3
.LBB0_7: # %middle.block.unr-lcssa
testq %r8, %r8
je .LBB0_9
# BB#8: # %middle.block.epilog-lcssa
vpmovzxwd 48(%rdx,%rax,2), %ymm4 # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpaddd %ymm3, %ymm4, %ymm3
vpmovzxwd 32(%rdx,%rax,2), %ymm4 # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpaddd %ymm2, %ymm4, %ymm2
vpmovzxwd 16(%rdx,%rax,2), %ymm4 # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpaddd %ymm1, %ymm4, %ymm1
vpmovzxwd (%rdx,%rax,2), %ymm4 # ymm4 = mem[0],zero,mem[1],zero,mem[2],zero,mem[3],zero,mem[4],zero,mem[5],zero,mem[6],zero,mem[7],zero
vpaddd %ymm0, %ymm4, %ymm0
.LBB0_9: # %middle.block
vpaddd %ymm0, %ymm1, %ymm0
vpaddd %ymm0, %ymm2, %ymm0
vpaddd %ymm0, %ymm3, %ymm0
vextracti128 $1, %ymm0, %xmm1
vpaddd %ymm1, %ymm0, %ymm0
vpshufd $78, %xmm0, %xmm1 # xmm1 = xmm0[2,3,0,1]
vpaddd %ymm1, %ymm0, %ymm0
vphaddd %ymm0, %ymm0, %ymm0
vmovd %xmm0, %eax
movq %r9, %rdi
cmpq %r9, %rcx
je .LBB0_11
.p2align 4, 0x90
.LBB0_10: # %for.body
# =>This Inner Loop Header: Depth=1
movzwl (%rdx,%rdi,2), %esi
addl %esi, %eax
addq $1, %rdi
cmpq %rcx, %rdi
jl .LBB0_10
.LBB0_11: # %for.cond.cleanup
vzeroupper
retq