在编译时知道for循环的迭代次数是否有优势?

时间:2012-10-18 12:03:22

标签: c optimization opencl

在编译时知道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不同。

5 个答案:

答案 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 exampleAMD 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
...