如果循环的迭代次数在编译时未知,GCC 如何展开循环?
How can GCC unroll a loop if its number of iterations is unknown at compile time?
当我找到选项 -funroll-all-loops
时,我正在阅读 optimization options for GCC。
它的描述是:
Unroll all loops, even if their number of iterations is uncertain when
the loop is entered. This usually makes programs run more slowly.
'-funroll-all-loops' implies the same options as '-funroll-loops'
如果循环的迭代次数在编译时未知,编译器如何展开循环?编译器不需要这些信息来展开它吗?它生成什么相应的 C 代码,如果它通常使程序 运行 变慢,这在什么情况下会有用?
您不能假设编译器的中间表示存在 对应的 C 代码 这样的东西。但在这种情况下,我希望最接近的等价物类似于 Duff's Device,这是一个可以在计算位置输入的序列(通常在循环中)。
它可以做类似的事情:
while(n >= 8){
foo(); foo(); foo(); foo(); foo(); foo(); foo(); foo();
n -= 8;
}
while(n > 0){
foo();
n--;
}
当然,Duff 的设备可以省去编写第二个循环的麻烦。
为什么要这样做?这取决于用户。
如果 foo()
花费多于几个周期, 或 如果原始循环花费的总挂钟时间少于 5%, 或 如果 n
通常很小,可能不值得这么麻烦。
这里有一些 C 代码展示了如何做到这一点:
int iterations = 100;
int unrollValue = 8;
while (iterations%unrollvalue)
{
// insert loop code here
iterations--;
}
while (iterations)
{
// insert unrollValue copies of loop code here
iterations-= unrollValue;
}
编译器将用相对跳转替换第一个循环,但这在 C 中不容易表示。请注意,按 2 的幂展开允许编译器使用掩码而不是(昂贵的)除法运算。
in what contexts could this be useful if it usually makes the programs run more slowly?
好吧,他们假设如果您选择此选项,您知道自己在做什么,否则您不应该使用此选项。
gcc 是做什么的,好吧,我使用了这个示例程序:
#include <stdio.h>
void f(int j )
{
for( int k = 0; k < j; ++k )
{
printf( "%d\n", k ) ;
}
}
并用 godbolt 测试它,它根据剩余的迭代次数生成跳转 table (see it live):
cmpl , %ebp
movl , %ebx
je .L1
testl %r12d, %r12d
je .L27
cmpl , %r12d
je .L28
cmpl , %r12d
je .L29
cmpl , %r12d
je .L30
cmpl , %r12d
je .L31
cmpl , %r12d
je .L32
cmpl , %r12d
je .L33
当我找到选项 -funroll-all-loops
时,我正在阅读 optimization options for GCC。
它的描述是:
Unroll all loops, even if their number of iterations is uncertain when the loop is entered. This usually makes programs run more slowly. '-funroll-all-loops' implies the same options as '-funroll-loops'
如果循环的迭代次数在编译时未知,编译器如何展开循环?编译器不需要这些信息来展开它吗?它生成什么相应的 C 代码,如果它通常使程序 运行 变慢,这在什么情况下会有用?
您不能假设编译器的中间表示存在 对应的 C 代码 这样的东西。但在这种情况下,我希望最接近的等价物类似于 Duff's Device,这是一个可以在计算位置输入的序列(通常在循环中)。
它可以做类似的事情:
while(n >= 8){
foo(); foo(); foo(); foo(); foo(); foo(); foo(); foo();
n -= 8;
}
while(n > 0){
foo();
n--;
}
当然,Duff 的设备可以省去编写第二个循环的麻烦。
为什么要这样做?这取决于用户。
如果 foo()
花费多于几个周期, 或 如果原始循环花费的总挂钟时间少于 5%, 或 如果 n
通常很小,可能不值得这么麻烦。
这里有一些 C 代码展示了如何做到这一点:
int iterations = 100;
int unrollValue = 8;
while (iterations%unrollvalue)
{
// insert loop code here
iterations--;
}
while (iterations)
{
// insert unrollValue copies of loop code here
iterations-= unrollValue;
}
编译器将用相对跳转替换第一个循环,但这在 C 中不容易表示。请注意,按 2 的幂展开允许编译器使用掩码而不是(昂贵的)除法运算。
in what contexts could this be useful if it usually makes the programs run more slowly?
好吧,他们假设如果您选择此选项,您知道自己在做什么,否则您不应该使用此选项。
gcc 是做什么的,好吧,我使用了这个示例程序:
#include <stdio.h>
void f(int j )
{
for( int k = 0; k < j; ++k )
{
printf( "%d\n", k ) ;
}
}
并用 godbolt 测试它,它根据剩余的迭代次数生成跳转 table (see it live):
cmpl , %ebp
movl , %ebx
je .L1
testl %r12d, %r12d
je .L27
cmpl , %r12d
je .L28
cmpl , %r12d
je .L29
cmpl , %r12d
je .L30
cmpl , %r12d
je .L31
cmpl , %r12d
je .L32
cmpl , %r12d
je .L33