【问题标题】:gcc loop unrolling odditygcc循环展开奇怪
【发布时间】:2016-11-04 21:30:54
【问题描述】:

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

// 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 会展开 s=s*2 循环, 但不是 s=s+s 循环。这有点不直观,因为 加法的循环计数分析应该,IMO 更简单 比乘法。我怀疑 gcc 确实知道 s=s+s 循环计数,只是害羞。

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

[展开的版本,顺便说一句,运行速度比循环慢一点。]

谢谢, 罗伯特

【问题讨论】:

    标签: gcc loop-unrolling


    【解决方案1】:

    这很有趣。

    初步猜测

    我的第一个猜测是 gcc 的循环展开分析预计加法案例从循环展开中受益较少,因为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;
    }
    

    另一个相同的版本除了循环有s = s + s。我发现 gcc 4.9.2 在乘法版本中展开循环,而不是在加法版本中展开循环。这是用

    编译的
    gcc -S -O3 test.c
    

    所以我的第一个猜测是 gcc 假设附加版本,如果展开,将导致更多字节的代码适合 icache,因此不会优化。但是,在加法版本中将循环条件从 s &lt; 32 更改为 s &lt; 4 仍然不会导致优化,尽管 gcc 似乎应该很容易认识到循环的迭代次数非常少。

    我的下一个尝试(回到s &lt; 32 作为条件)是明确告诉 gcc 展开循环最多 100 次:

    gcc -S -O3 -fverbose-asm --param max-unroll-times=100 test.c
    

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

    有趣的是,尝试在-O3 使用clang 编译会立即展开循环。 clang 是 known to unroll more aggressively,但这似乎不是一个令人满意的答案。

    我可以让 gcc 展开加法循环,方法是让它添加一个常量而不是 s 本身,也就是说,我做 s = s + 2。然后循环展开。

    第二次猜测

    这导致我推测如果循环的增加值不止一次取决于计数器的值,gcc 无法理解循环将运行多少次迭代(展开所必需的)。我将循环更改如下:

    for (s = 2; s < 32; s = s*s)
    

    它不会使用 gcc 展开,而 clang 会展开它。所以最后我最好的猜测是,当循环的增量语句的形式为s = s (op) s 时,gcc 无法计算迭代次数。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        猜你喜欢
        • 2016-10-24
        • 1970-01-01
        • 2017-01-21
        • 2019-09-18
        • 1970-01-01
        • 1970-01-01
        • 2014-11-10
        • 1970-01-01
        相关资源
        最近更新 更多