14.9.3叶子函数
所谓叶子函数(leaffunction)就是在其函数体内不存在对其他函数调用,它也常被称为终级函数。因为叶子函数不需要调用其他函数,所有没有保存/恢复寄存器的操作,因此执行效率比一般函数要高。
当函数中必须对一些寄存器进行保存时,可以使用高效率的多寄存器存储指令STM,对需要保存的寄存器内存一次性存储。
正是由于叶子函数执行的高效性,所以在编程时,尽量将子程序编写为叶子函数,这样即使程序中多次调用也不会影响代码性能。
为了高效的调用函数,可以遵循下面函数调用原则。
·避免在被频繁调用的函数中调用其他函数,以保证被频繁调用的函数被编译器编译为叶子函数。
·把比较小的被调用函数和调用函数放在同一个源文件中,并且要先定义后调用,编译器就可以优化函数调用或内联较小的函数。
·对性能影响较大的重要函数可使用关键字_inline进行内联。
14.9.4嵌套优化
注意
嵌套优化(Tail-Calloptimization)只适用于armcc。编译时如果使用-g或-debug选项,编译器自动关闭该功能。
一个函数如果在其结束时调用了另一个函数,则编译器使用B指令调转到被调用函数,而非BL指令。这样就避免了一级不必要的函数返回。图14.3显示了嵌套优化的调用过程。
图14.3嵌套优化函数调用过程
当编译时使用-O1或-O2选项时,编译器都执行这种嵌套优化。需要注意的是,当函数中引用了局部变量地址,由于指针别名问题的影响,即使函数在返回时调用了其他函数,编译器也不会使用嵌套优化。
下面通过一个例子来分析嵌套优化是如何提高代码执行效率的。
externintfunc2(int);
intfunc1(inta,intb)
{if(a>b)
return(func2(a-b));
else
return(func2(b-a));
}
编译后的代码如下所示。
func1
CMPa1,a2
SUBLEa1,a2,a1
SUBGTa1,a1,a2
Bfunc2
首先,func1中使用B指令代替BL指令,不用担心lr寄存器被破坏,减少了对寄存器压栈保护操作。另外,程序直接从func2返回到调用func1的函数,减少一次函数返回。如果说正常的指令调用过程为:
BL+BL+MOVpc,lr+MOVpc,lr
那么经过嵌套优化的函数调用过程就可以表示为:
BL+BL+MOVpc,lr
这样,总的开销将减少25%。