在编译时知道for循环应该进行多少次迭代是否有一些优势?
例如,在某些情况下,编译器可以生成一个可执行程序,运行速度更快,如下所示:
#define ITERATIONS 10
int foo()
{
for (int i=0; i < ITERATIONS; i++){
do_something();
}
}
比给出这个:
int foo(int iterations)
{
for (int i=0; i < iterations; i++){
do_something();
}
}
如果情况不是普遍存在,那么这些情况是什么?
我关注的是OpenCL的具体情况,所以我也有兴趣知道这是否与C不同。
答案 0 :(得分:7)
我使用GCC在相当现实的情况下进行了测试。当在编译时知道循环数时,我得到:
.L2:
call do_something
subl $1, %ebx
jne .L2
如果不是,我明白了:
.L6:
call do_something
addl $1, %ebx
cmpl %ebp, %ebx
jne .L6
因此,通过将计数降低到零循环而不是向上计数循环,它能够稍微优化固定的迭代次数。如果不出意外,这会使用更少的代码缓存。
具有更高的优化级别,它完全展开一个调用外部函数十次的循环。据推测,除非它认为它更好,否则它不会那样做。如果迭代次数未知,肯定无法做到这一点。
简短回答:固定的迭代次数为编译器提供了更多选择。至少在某些时候,这应该会产生非常好的代码。
答案 1 :(得分:4)
确实,这取决于编译器。但实际上它允许展开循环。 这是Intel example和AMD example。
使用NVIDIA,您需要在内核中使用#pragma OPENCL EXTENSION cl_nv_pragma_unroll : enable
。
因此,您可以在编译时使用标记,例如:-DITERATIONS=10
答案 2 :(得分:3)
您需要初始化i
,
for (int i; i < ITERATIONS; i++){
是未定义的行为,允许编译器完全跳过循环;)
除此之外,如果在编译时已知迭代次数,编译器可以完全展开循环,例如,这可能会产生很大的不同(如果循环体很便宜)。
答案 3 :(得分:1)
这很大程度上取决于您使用的编译器和优化设置。最有可能的是,知道循环的数量将允许编译器展开循环并摆脱分支和参数i。
但这完全取决于编译器,所以不要指望它。但是如果你有一个现代的编译器,很有可能会提高速度。
编辑:
我读过了显而易见的未初始化的我,但你可能也做了......
答案 4 :(得分:1)
对于gcc-4.5.3,do_something() {printf("hello world")}
:
-O2
,编译器不会展开任何循环。-O4
,对于预先知道的迭代次数,编译器会展开循环并内联do_something()
:
foo:
...
.L4:
addl $1, %ebx
movl $.LC0, 4(%esp)
movl $1, (%esp)
call __printf_chk
cmpl %ebx, %esi
jg .L4
main:
...
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $.LC0, 4(%esp)
movl $1, (%esp)
call __printf_chk
movl $.LC0, 4(%esp)
movl $1, (%esp)
call __printf_chk
movl $.LC0, 4(%esp)
movl $1, (%esp)
call __printf_chk
...