C编译器通过运行它来优化循环

时间:2015-07-14 23:06:25

标签: c loops for-loop optimization compiler-optimization

C编译器可以通过运行来优化循环吗?

例如:

int num[] = {1, 2, 3, 4, 5}, i;
for(i = 0; i < sizeof(num)/sizeof(num[0]); i++) {
  if(num[i] > 6) {
    printf("Error in data\n");
    exit(1);
  }
}

不是每次执行程序时都运行它,编译器可以简单地运行它并优化它吗?

4 个答案:

答案 0 :(得分:4)

让我们来看看......(这确实是唯一可以判断的方法。)

Fist,我已将您的代码段转换为我们实际可以尝试编译和运行的内容,并将其保存在名为main.c的文件中。

#include <stdio.h>

static int
f()
{
  const int num[] = {1, 2, 3, 4, 5};
  int i;
  for (i = 0; i < sizeof(num) / sizeof(num[0]); i++)
    {
      if (num[i] > 6)
        {
          printf("Error in data\n");
          return 1;
        }
    }
  return 0;
}

int
main()
{
  return f();
}

运行gcc -S -O3 main.c会生成以下程序集文件(在main.s中)。

        .file   "main.c"
        .section        .text.unlikely,"ax",@progbits
.LCOLDB0:
        .section        .text.startup,"ax",@progbits
.LHOTB0:
        .p2align 4,,15
        .globl  main
        .type   main, @function
main:
.LFB22:
        .cfi_startproc
        xorl    %eax, %eax
        ret
        .cfi_endproc
.LFE22:
        .size   main, .-main
        .section        .text.unlikely
.LCOLDE0:
        .section        .text.startup
.LHOTE0:
        .ident  "GCC: (GNU) 5.1.0"
        .section        .note.GNU-stack,"",@progbits

即使您不知道汇编,您也会注意到文件中没有字符串"Error in data\n",因此显然必须进行某种优化。

如果我们仔细查看为main函数生成的机器指令,

xorl    %eax, %eax
ret

我们可以看到它所做的只是将EAX寄存器与其自身进行异或(总是导致零)并将该值写入EAX。然后它又回来了。 EAX寄存器用于保存返回值。我们可以看到,f函数已经完全优化了。

答案 1 :(得分:1)

答案 2 :(得分:0)

您没有指定编译器,但是使用gcc和-O3 以及可能之外进行大小计算,它可以做一些调整。

答案 3 :(得分:0)

编译器可以做得更好。编译器不仅可以检查运行代码&#34; forward&#34;的效果,而且标准甚至允许它们在涉及潜在未定义行为的情况下以反向方式处理代码逻辑。例如,给定:

#include <stdio.h>
int main(void)
{
  int ch = getchar();
  int q;
  if (ch == 'Z')
    q=5;
  printf("You typed %c and the magic value is %d", ch, q);
  return 0;
}

编译器有权假设程序永远不会收到任何会导致printf在没有q收到值的情况下到达的输入;因为唯一会导致q接收值的输入字符将是'Z',因此编译器可以合法地用以下代码替换代码:

int main(void)
{
  getchar();
  printf("You typed Z and the magic value is 5");
}

如果用户键入Z,原始程序的行为将被明确定义,后者的行为将与之匹配。如果用户键入其他任何内容,原始程序将调用未定义的行为,因此,标准将不对编译器可能执行的操作施加任何要求。编译器有权做任何喜欢的事情,包括产生与键入Z产生的结果相同的结果。