【发布时间】:2019-03-22 14:35:15
【问题描述】:
我对这个简单示例中使用有符号和无符号循环计数器的区别感到非常惊讶:
double const* a;
__assume_aligned(a, 64);
double s = 0.0;
//for ( unsigned int i = 0; i < 1024*1024; i++ )
for ( int i = 0; i < 1024*1024; i++ )
{
s += a[i];
}
在签名的情况下,产生了 icc 19.0.0(我正在展示循环的展开部分):
..B1.2:
vaddpd zmm7, zmm7, ZMMWORD PTR [rdi+rax*8]
vaddpd zmm6, zmm6, ZMMWORD PTR [64+rdi+rax*8]
vaddpd zmm5, zmm5, ZMMWORD PTR [128+rdi+rax*8]
vaddpd zmm4, zmm4, ZMMWORD PTR [192+rdi+rax*8]
vaddpd zmm3, zmm3, ZMMWORD PTR [256+rdi+rax*8]
vaddpd zmm2, zmm2, ZMMWORD PTR [320+rdi+rax*8]
vaddpd zmm1, zmm1, ZMMWORD PTR [384+rdi+rax*8]
vaddpd zmm0, zmm0, ZMMWORD PTR [448+rdi+rax*8]
add rax, 64
cmp rax, 1048576
jb ..B1.2 # Prob 99%
在无符号情况下,icc 使用额外的寄存器来寻址内存,对应的LEAs:
..B1.2:
lea edx, DWORD PTR [8+rax]
vaddpd zmm6, zmm6, ZMMWORD PTR [rdi+rdx*8]
lea ecx, DWORD PTR [16+rax]
vaddpd zmm5, zmm5, ZMMWORD PTR [rdi+rcx*8]
vaddpd zmm7, zmm7, ZMMWORD PTR [rdi+rax*8]
lea esi, DWORD PTR [24+rax]
vaddpd zmm4, zmm4, ZMMWORD PTR [rdi+rsi*8]
lea r8d, DWORD PTR [32+rax]
vaddpd zmm3, zmm3, ZMMWORD PTR [rdi+r8*8]
lea r9d, DWORD PTR [40+rax]
vaddpd zmm2, zmm2, ZMMWORD PTR [rdi+r9*8]
lea r10d, DWORD PTR [48+rax]
vaddpd zmm1, zmm1, ZMMWORD PTR [rdi+r10*8]
lea r11d, DWORD PTR [56+rax]
add eax, 64
vaddpd zmm0, zmm0, ZMMWORD PTR [rdi+r11*8]
cmp eax, 1048576
jb ..B1.2 # Prob 99%
对我来说,令人惊讶的是它没有生成相同的代码(给定编译时循环计数)。是编译器优化问题吗?
编译选项:
-O3 -march=skylake-avx512 -mtune=skylake-avx512 -qopt-zmm-usage=high
【问题讨论】:
-
编译器似乎无法推断出截断到 32 位实际上是没有必要的。它如何与
size_t一起使用? -
这是一个错过的优化,因为它可以证明
i不会换行但会失败。但是有符号溢出是 UB,它可以将 32 位循环计数器提升到 64 位,而无需每次都重做符号扩展,就像它每次在这里重做零扩展一样。不是每次,而是每 8 次。 -
@zch 你是对的,
size_t产生与signed int 相同的代码
标签: assembly optimization compiler-optimization icc unsigned-integer