在for语句参数内部的C / C ++算术运算中

时间:2016-04-13 20:05:37

标签: c++ c for-loop const compiler-optimization

我们说我有这段代码:

int v;
setV(&v);
for (int i = 0; i < v - 5; i++) {
  // Do stuff here, but don't use v.
}

每次都会运行操作v - 5,还是现代编译器足够聪明,可以存储一次而不再运行它?

如果我这样做了怎么办:

int v;
setV(&v);
const int cv = v;
for (int i = 0; i < cv - 5; i++) {
  // Do stuff here. Changing cv is actually impossible.
}

第二种风格会有所作为吗?

1 个答案:

答案 0 :(得分:4)

编辑:

出于意想不到的原因,这是一个有趣的问题。这更像是编译器避免v的意外别名的钝化情况的问题。如果编译器可以证明这不会发生(版本2)那么我们会得到更好的代码。

这里的教训是更加关注消除别名,而不是尝试为此做优化工作。

使复制cv实际上提供了最大的优化(冗余内存提取的省略),即使乍一看它似乎(略微)效率低。

原始答案和演示:

让我们看看:

下式给出:

extern void setV(int*);
extern void do_something(int i);

void test1()
{
    int v;
    setV(&v);
    for (int i = 0; i < v - 5; i++) {
    // Do stuff here, but don't use v.
      do_something(i);
    }
}


void test2()
{
    int v;
    setV(&v);
    const int cv = v;
    for (int i = 0; i < cv - 5; i++) {
      // Do stuff here. Changing cv is actually impossible.
      do_something(i);
    }  
}

使用-x c++ -std=c++14 -O2 -Wall

在gcc5.3上编译

给出:

test1():
        pushq   %rbx
        subq    $16, %rsp
        leaq    12(%rsp), %rdi
        call    setV(int*)
        cmpl    $5, 12(%rsp)
        jle     .L1
        xorl    %ebx, %ebx
.L5:
        movl    %ebx, %edi
        addl    $1, %ebx
        call    do_something(int)
        movl    12(%rsp), %eax
        subl    $5, %eax
        cmpl    %ebx, %eax
        jg      .L5
.L1:
        addq    $16, %rsp
        popq    %rbx
        ret
test2():
        pushq   %rbp
        pushq   %rbx
        subq    $24, %rsp
        leaq    12(%rsp), %rdi
        call    setV(int*)
        movl    12(%rsp), %eax
        cmpl    $5, %eax
        jle     .L8
        leal    -5(%rax), %ebp
        xorl    %ebx, %ebx
.L12:
        movl    %ebx, %edi
        addl    $1, %ebx
        call    do_something(int)
        cmpl    %ebp, %ebx
        jne     .L12
.L8:
        addq    $24, %rsp
        popq    %rbx
        popq    %rbp
        ret

第二种形式在这个编译器上更好。