C 编译器通过 运行 优化循环
C compiler optimize loop by running it
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);
}
}
不是每次执行程序时都运行宁这个,编译器可以简单地运行这个并优化它吗?
您没有指定编译器,但是将 gcc 与 -O3 and 一起使用,在 for maybe 之外进行大小计算可能稍微调整一下。
是的。 C 编译器 unrolls loops automatically with options -O3 and -Otime.
编译器甚至可以做得更好。编译器不仅可以检查 运行 代码 "forward" 的效果,而且标准甚至允许它们在涉及潜在未定义行为的情况下 反向 工作代码逻辑。例如,给定:
#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;
}
编译器有权假设程序永远不会接收任何输入,这会导致在 q
没有接收到值的情况下到达 printf
;由于唯一会导致 q
接收值的输入字符是 'Z'
,因此编译器可以合法地将代码替换为:
int main(void)
{
getchar();
printf("You typed Z and the magic value is 5");
}
如果用户键入Z
,原程序的行为将被明确定义,而后者的行为将与之匹配。如果用户键入任何其他内容,原始程序将调用未定义行为,因此,标准将不会对编译器可以做什么施加任何要求。编译器将有权做任何它喜欢的事情,包括产生与键入 Z
.
产生的结果相同的结果。
让我们看看……(真的只能这样说了。)
拳头,我已将您的代码片段转换为我们可以实际尝试编译的内容,运行 并将其保存在名为 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。然后它再次 returns。 EAX 寄存器用于保存 return 值。正如我们所见,f
函数被完全优化掉了。
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);
}
}
不是每次执行程序时都运行宁这个,编译器可以简单地运行这个并优化它吗?
您没有指定编译器,但是将 gcc 与 -O3 and 一起使用,在 for maybe 之外进行大小计算可能稍微调整一下。
是的。 C 编译器 unrolls loops automatically with options -O3 and -Otime.
编译器甚至可以做得更好。编译器不仅可以检查 运行 代码 "forward" 的效果,而且标准甚至允许它们在涉及潜在未定义行为的情况下 反向 工作代码逻辑。例如,给定:
#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;
}
编译器有权假设程序永远不会接收任何输入,这会导致在 q
没有接收到值的情况下到达 printf
;由于唯一会导致 q
接收值的输入字符是 'Z'
,因此编译器可以合法地将代码替换为:
int main(void)
{
getchar();
printf("You typed Z and the magic value is 5");
}
如果用户键入Z
,原程序的行为将被明确定义,而后者的行为将与之匹配。如果用户键入任何其他内容,原始程序将调用未定义行为,因此,标准将不会对编译器可以做什么施加任何要求。编译器将有权做任何它喜欢的事情,包括产生与键入 Z
.
让我们看看……(真的只能这样说了。)
拳头,我已将您的代码片段转换为我们可以实际尝试编译的内容,运行 并将其保存在名为 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。然后它再次 returns。 EAX 寄存器用于保存 return 值。正如我们所见,f
函数被完全优化掉了。