转载请注明来源 http://blog.csdn.net/imred/article/details/48865359
初学C语言的时候,我们有时会听说函数调用会有一定的开销,在进行了进一步学习之后,我们来看看原来听说的开销指的什么。
下面是两个非常简单的样例,就不作解释了:
函数调用版本C程序:
#include <stdio.h>
int sum(int a, int b)
{
return a + b;
}
int main()
{
int a = 1;
int b = 1;
int c;
c = sum(a, b);
return 0;
}
使用gcc汇编后:
.file "function.c"
.text
.globl _sum
.def _sum; .scl 2; .type 32; .endef
_sum:
pushl %ebp
movl %esp, %ebp
movl 12(%ebp), %eax
movl 8(%ebp), %edx
addl %edx, %eax
popl %ebp
ret
.def ___main; .scl 2; .type 32; .endef
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
call ___main
movl $1, 28(%esp)
movl $1, 24(%esp)
movl 24(%esp), %eax
movl %eax, 4(%esp)
movl 28(%esp), %eax
movl %eax, (%esp)
call _sum
movl %eax, 20(%esp)
movl $0, %eax
leave
ret
无函数调用版本c程序:
#include <stdio.h>
int main()
{
int a = 1;
int b = 1;
int c;
c = a + b;
return 0;
}
使用gcc汇编后:
.file "no_function.c"
.def ___main; .scl 2; .type 32; .endef
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
call ___main
movl $1, 12(%esp)
movl $1, 8(%esp)
movl 8(%esp), %eax
movl 12(%esp), %edx
addl %edx, %eax
movl %eax, 4(%esp)
movl $0, %eax
leave
ret
除了公共部分和指导信息之外,第一个汇编程序比第二个汇编程序多了以下内容:
1
movl 24(%esp), %eax
movl %eax, 4(%esp)
movl 28(%esp), %eax
movl %eax, (%esp)
2
call _sum
3
pushl %ebp
movl %esp, %ebp
4
popl %ebp
ret
额外的开销就体现在这4段代码上了,我来一一解释一下它们的作用:
1
参数入栈代码,将函数参数入栈,这是现在函数调用的标准方式。参数越多,开销越大
2
将控制权转移至函数中
3
建立新的栈帧,也就是当前函数使用的“一片”栈空间,使用ebp的值来标识新的栈帧,因此要将原栈帧首地址保存下来,方便回到原来的即调用者的栈帧
4
恢复原栈帧,然后将控制权转移至调用者
从汇编的角度来看,这额外的开销就是指的这不足十行的指令了,会对性能有多大影响呢?大概有那么一丁点。当然,对于不同的函数,以及不同的调用频率,这个影响也不尽相同。当然,这个程序是一个极端,它函数的功能代码也只有3行,这样的情况完全可以不用函数或者使用内联函数。所以是使用函数还是不使用函数呢?依我来看,为了程序的可读性,我们还是牺牲那么一丁点的性能吧。
转载请注明来源 http://blog.csdn.net/imred/article/details/48865359