C语言编码:临时局部变量的效率
我在想:用C编程,假设我们有两个函数:C语言编码:临时局部变量的效率,c,function,variables,coding-style,C,Function,Variables,Coding Style,我在想:用C编程,假设我们有两个函数: int get_a_value(); int calculate_something(int number); 第三个的两个版本: /* version 1 */ int main() { int value = get_a_value(); int result = calculate_something(value); return result; } /* version 2 */ int main() { int
int get_a_value();
int calculate_something(int number);
第三个的两个版本:
/* version 1 */
int main()
{
int value = get_a_value();
int result = calculate_something(value);
return result;
}
/* version 2 */
int main()
{
int result = calculate_something(get_a_value());
return result;
}
从理论上讲,同一事物的这两个版本在正确性、内存使用和效率方面有什么不同?它们会生成不同的指令吗?另一方面,什么情况会使可能的差异在现实中变得显著
提前感谢。我执行了一个小测试,为这两个版本生成汇编代码。简单地从bash运行diff命令表明,第一个版本比第二个版本多2条指令。
如果您想自己尝试,只需使用此命令编译即可
gcc -S main.c -o asmout.s
gcc -S main2.c -o asmout2.s
然后用
diff asmout.s asmout2.s
我得到了以下两个说明,第一个是:
movl %eax, -8(%rbp)
movl -8(%rbp), %eax
编辑:正如Keith Thompson所建议的,如果使用优化选项编译,生成的汇编代码对于两个版本都是相同的。我执行了一个小测试,为两个版本生成汇编代码。简单地从bash运行diff命令表明,第一个版本比第二个版本多2条指令。
如果您想自己尝试,只需使用此命令编译即可
gcc -S main.c -o asmout.s
gcc -S main2.c -o asmout2.s
然后用
diff asmout.s asmout2.s
我得到了以下两个说明,第一个是:
movl %eax, -8(%rbp)
movl -8(%rbp), %eax
编辑:正如Keith Thompson所建议的,如果使用优化选项编译,生成的汇编程序代码对于两个版本都是相同的。这实际上取决于平台和编译器,但如果使用优化选项,它们通常会生成相同的代码。在最坏的版本中,一个会为一个额外的int分配空间。如果将get_a_value的值放在一个变量中使代码更具可读性,那么我会继续这样做。我唯一建议不要这样做的时候是在一个深度递归函数中。这实际上取决于平台和编译器,但在进行优化后,它们通常会生成相同的代码。在最坏的版本中,一个会为一个额外的int分配空间。如果将get_a_value的值放在一个变量中使代码更具可读性,那么我会继续这样做。我唯一建议不要这样做的时候是在一个深度递归函数中。复制了两个版本,分别用
gcc-S
编译以获得机器语言输出,使用sdiff
进行并排比较
使用gcc版本4.1.2 20070115(SUSE Linux)的结果:
无优化:
main: main:
.LFB2: .LFB2:
pushq %rbp pushq %rbp
.LCFI0: .LCFI0:
movq %rsp, %rbp movq %rsp, %rbp
.LCFI1: .LCFI1:
subq $16, %rsp subq $16, %rsp
.LCFI2: .LCFI2:
movl $0, %eax movl $0, %eax
call get_a_value call get_a_value
movl %eax, -8(%rbp) | movl %eax, %edi
movl -8(%rbp), %edi <
movl $0, %eax movl $0, %eax
call calculate_something call calculate_something
movl %eax, -4(%rbp) movl %eax, -4(%rbp)
movl -4(%rbp), %eax movl -4(%rbp), %eax
leave leave
ret ret
main: main:
pushl %ebp pushl %ebp
movl %esp, %ebp movl %esp, %ebp
subl $8, %esp subl $8, %esp
> subl $12, %esp
> subl $4, %esp
call get_a_value call get_a_value
> addl $4, %esp
movl %eax, %eax movl %eax, %eax
movl %eax, -4(%ebp) | pushl %eax
subl $12, %esp <
pushl -4(%ebp) <
call calculate_something call calculate_something
addl $16, %esp addl $16, %esp
movl %eax, %eax movl %eax, %eax
movl %eax, -8(%ebp) | movl %eax, -4(%ebp)
movl -8(%ebp), %eax | movl -4(%ebp), %eax
movl %eax, %eax movl %eax, %eax
leave leave
ret ret
没有区别
使用gcc版本2.96 20000731(Red Hat Linux 7.2 2.96-112.7.2)的结果:
无优化:
main: main:
.LFB2: .LFB2:
pushq %rbp pushq %rbp
.LCFI0: .LCFI0:
movq %rsp, %rbp movq %rsp, %rbp
.LCFI1: .LCFI1:
subq $16, %rsp subq $16, %rsp
.LCFI2: .LCFI2:
movl $0, %eax movl $0, %eax
call get_a_value call get_a_value
movl %eax, -8(%rbp) | movl %eax, %edi
movl -8(%rbp), %edi <
movl $0, %eax movl $0, %eax
call calculate_something call calculate_something
movl %eax, -4(%rbp) movl %eax, -4(%rbp)
movl -4(%rbp), %eax movl -4(%rbp), %eax
leave leave
ret ret
main: main:
pushl %ebp pushl %ebp
movl %esp, %ebp movl %esp, %ebp
subl $8, %esp subl $8, %esp
> subl $12, %esp
> subl $4, %esp
call get_a_value call get_a_value
> addl $4, %esp
movl %eax, %eax movl %eax, %eax
movl %eax, -4(%ebp) | pushl %eax
subl $12, %esp <
pushl -4(%ebp) <
call calculate_something call calculate_something
addl $16, %esp addl $16, %esp
movl %eax, %eax movl %eax, %eax
movl %eax, -8(%ebp) | movl %eax, -4(%ebp)
movl -8(%ebp), %eax | movl -4(%ebp), %eax
movl %eax, %eax movl %eax, %eax
leave leave
ret ret
main:main:
推送%ebp推送%ebp
移动%esp,%ebp移动%esp,%ebp
子项$8,%esp子项$8,%esp
>12美元,特别是
>次级$4,%esp
调用获取值调用获取值
>加4美元,特别是
移动%eax,%eax移动%eax,%eax
移动%eax,-4%(ebp)|推送%eax
12美元,特别是<
PUSH-4(%ebp)<
呼叫计算某物呼叫计算某物
加成$16%esp加成$16%esp
移动%eax,%eax移动%eax,%eax
movl%eax,-8(%ebp)| movl%eax,-4(%ebp)
movl-8(%ebp),%eax | movl-4(%ebp),%eax
移动%eax,%eax移动%eax,%eax
离开
ret-ret
指令数量大致相同,顺序略有不同
一级优化(-O1):
main:main:
推送%ebp推送%ebp
移动%esp,%ebp移动%esp,%ebp
次级美元8%,特别是|次级美元24%,特别是
调用获取值调用获取值
次级贷款$12%,esp |动产%eax,(%esp)
pushl%eax<
呼叫计算某物呼叫计算某物
离开
ret-ret
看起来第二个版本保留了更多的堆栈空间
因此,对于这个使用这些特定编译器的特定示例,这两个版本之间没有太大的差异。在这种情况下,我倾向于第一个版本,原因如下:
get_a_value
返回的值,然后将其传递给calculate_something
李>
计算某些东西对于某些输入来说表现不好李>
这对眼睛比较容易李>
请记住,简练并不一定意味着快速或高效,在一个特定的编译器/硬件组合下快速/高效的东西在另一个编译器/硬件组合下可能会被彻底破坏。一些编译器实际上可以更轻松地优化以清晰方式编写的代码
您的代码应按顺序为:
正确-无论它的速度有多快,或者如果它不满足它的要求,它使用的内存有多少李>
安全-无论它的速度有多快,也不管它使用的内存有多少,如果它是恶意软件载体或有暴露的风险