似乎感应变量消除是一个众所周知的编译器转换,但我不能让英特尔编译器或GCC去做。
https://www.cs.utexas.edu/~pingali/CS380C/2013/lectures/strengthReduction.pdf
这是我的测试计划:
int main(int argc, char **argv)
{
typedef int64_t /*int32_t*/ LoopType;
LoopType REPEATS = 1000000, N = atoi(argv[1]);
int16_t *data = (int16_t *)alloca(N * sizeof(int16_t));
for (int j = 0; j < REPEATS; ++j)
{
for (LoopType i = 0; i < N; i += (LoopType)8)
{
__m128i d = _mm_loadu_si128((__m128i *)&data[i]);
d = _mm_add_epi16(d, d);
_mm_storeu_si128((__m128i *)&data[i], d);
}
}
return data[5];
}
最内层循环的汇编代码:
..B1.4: # Preds ..B1.2 ..B1.4
movdqu (%rsi), %xmm0 #50.30
incq %rdi #53.5
paddw %xmm0, %xmm0 #56.11
movdqu %xmm0, (%rsi) #50.30
addq $16, %rsi #53.5
cmpq %rdx, %rdi #53.5
jb ..B1.4 # Prob 82% #53.5
我想要的是编译器用指针表达式&amp; data [i]替换循环计数器i。我认识到存在阻止编译器进行转换的问题(溢出)。但我也发现GCC有一个标志-funsafe-loop-optimizations,假设溢出不会发生。
但这并没有帮助。我尝试使用ICC 14和GCC 4.9编译上面的代码,它总是产生2个感应变量(通常是7个指令),而如果我手动使用指针作为循环条件则为6个。
我尝试将循环类型从int64更改为int32,并将for循环更改为do - while(少1个分支),但这也没有帮助。
性能方面,我发现保存1条指令没有区别:
Linux的输出输出为N = 32768
8,687,218,116 cycles # 2.103 GHz
485,770,953 stalled-cycles-frontend # 5.59% frontend cycles idle
29,257,198 stalled-cycles-backend # 0.34% backend cycles idle
28,684,643,143 instructions # 3.30 insns per cycle
用指针替换循环条件i后,输出Linux的N = 32768
8,292,816,300 cycles # 2.102 GHz
102,773,625 stalled-cycles-frontend # 1.24% frontend cycles idle
29,845,621 stalled-cycles-backend # 0.36% backend cycles idle
24,586,051,519 instructions # 2.96 insns per cycle
这是有道理的,因为指令级并行意味着额外的添加不会增加关键路径。但它会释放一个寄存器,这是一件好事。
我也意识到,如果你有&gt;在循环中有1个数组表达式,然后归纳变量消除不会有助于减少#指令,因为有1个计数器并使用寄存器+寄存器寻址进行数组索引会更便宜。
所以似乎诱导变量消除的好处是有限的。有人可以确认是否在任何编译器中实现了归纳变量以及如何使其工作?我想知道,因为我必须决定是否手动进行这种优化而不利于可读性,或者继续使用计数器,并希望未来的编译器能够对其进行优化。