gcc循环展开奇怪

时间:2016-11-04 21:30:55

标签: gcc loop-unrolling

在为布尔数组编写“不等扫描”的过程中, 我最后写了这个循环:

// Heckman recursive doubling
#ifdef STRENGTHREDUCTION // Haswell/gcc does not like the multiply
    for( s=1; s<BITSINWORD; s=s*2) { 
#else // STRENGTHREDUCTION
    for( s=1; s<BITSINWORD; s=s+s) { 
#endif // STRENGTHREDUCTION
      w = w XOR ( w >> s);
    }

我观察到的是gcc WOULD展开s = s * 2循环, 但不是s = s + s循环。这有点不直观,如 对于添加的循环计数分析应该,IMO更简单 而不是乘法。我怀疑gcc知道s = s + s 循环计数,只是腼腆。

有没有人知道这是否有充分理由 gcc的行为? 我出于好奇而问这个......

[展开的版本,BTW,比循环慢了一点。]

谢谢, 罗伯特

2 个答案:

答案 0 :(得分:0)

这很有趣。

首先猜测

我的第一个猜测是gcc的循环展开分析预计添加案例会从循环展开中受益更少,因为interMarket增长得更慢。

我试验了以下代码:

s

除了循环之外,另一个版本是#include <stdio.h> int main(int argc, char **args) { int s; int w = 255; for (s = 1; s < 32; s = s * 2) { w = w ^ (w >> s); } printf("%d", w); // To prevent everything from being optimized away return 0; } 。我发现gcc 4.9.2在乘法版本中展开循环而不是添加循环。这是用

编译的
s = s + s

所以我的第一个猜测是gcc假设添加版本,如果展开,将导致更多字节的代码适合icache,因此不优化。但是,在添加版本中将循环条件从gcc -S -O3 test.c 更改为s < 32仍然不会导致优化,即使看起来gcc应该很容易识别循环的迭代非常少

我的下一次尝试(返回s < 4作为条件)是明确告诉gcc最多展开循环100次:

s < 32

这仍然会在装配中产生一个循环。尝试使用--param max-unrolled-insns在展开的循环中允许更多指令也保留循环。因此,我们几乎可以消除gcc认为展开效率低下的可能性。

有趣的是,尝试使用gcc -S -O3 -fverbose-asm --param max-unroll-times=100 test.c 处的clang进行编译会立即展开循环。 clang是known to unroll more aggressively,但这似乎不是一个令人满意的答案。

我可以让gcc通过添加一个常量而不是-O3本身来展开添加循环,也就是说,我做s。然后循环展开。

第二次猜测

这导致我理论上,如果循环的增加值多次取决于计数器的值,那么gcc无法理解循环将运行多少次迭代(展开所必需的)。我按如下方式更改循环:

s = s + 2

它没有展开gcc,而clang展开它。所以我最好的猜测是,当循环的增量语句的格式为for (s = 2; s < 32; s = s*s) 时,gcc无法计算迭代次数。

答案 1 :(得分:0)

编译器通常会执行强度降低,所以我希望如此 gcc会在这里使用它,用s + s替换s * 2,此时两者的形式 源代码表达式将匹配。

如果不是这样,那么我认为这是gcc中的一个错误。分析 使用s + s计算循环计数(略微)比这简单 使用s * 2,所以我希望gcc会(略) 更有可能展开s + s案例。