C55x的C/C++编译器中含有一个称为优化器(optimizer)的程序模块。优化器通过执行一些操作(如简化循环,重新安排语句和表达式,把变量用寄存器实现等),可以提高C/C++程序的运行速度,减少其代码长度。
1编译器的优化选项
1.1.基本优化选项
–o0:采取的主要优化措施有:简化控制流程,把变量安排到寄存器,简化循环,忽略未用代码,简化语句和表达式,把调用函数扩展为内嵌函数等。
–o1:在–o0级优化的基础上,进一步采取局部优化措施,如:进行COPY扩展,删除未用分配,忽略局部公共表达式等。
–o2:在–o1级优化的基础上,进一步采取全局部优化措施,如:进行循环优化,删除全局公共子表达式,删除全局未用分配等。
–o3:这是最大可能的优化级别。在–o2级优化的基础上,进一步进行的主要优化措施包括:对于从未调用的函数移除其代码,对于从未使用返回值的函数删除其返回代码,把小函数代码自动嵌入到程序中(参考-oi选项),重新安排函数声明的次序等。
-oi <size>:当采用–o3级优化时,优化器自动嵌入被调用的小函数。只有小于size的函数才能被嵌入。
1.2.文件级(File-Level)优化选项
–o3选项使编译器进行文件级优化。可以单独使用–o3选项进行普通的文件级优化,也可以与其它选项组合起来进行更专门的优化
要想控制文件级优化,可以使用–ol选项
–ol0:告诉编译器在源程序文件中声明了一个与标准库函数同名的函数,更改相应的库函数
–ol1:告诉编译器在源程序文件中声明一个与标准库函数同名的函数
–ol2:告诉编译器在源程序文件中不声明或改变任何标准库函数。当在命令文件或环境变量中选择了–ol0或–ol1选项时,可以通过–ol2选项取消–ol0或–ol1选项
采用–o3选项时,可以使用–on选项产生一个扩展名为.nfo的优化信息文件
–on0:取消优化信息文件的作用
–on1:产生优化信息文件
–on2:产生详细的优化信息文件
1.3.程序级(Program-Level)优化选项
通过使用-pm选项和-o3选项就可以进行程序级优化。通过程序级优化,所有源文件都被编译到一个中间文件中。这个中间文件提供给编译器在编译过程中完整的程序总览。因为编译器能够访问整个程序,因此它会执行一些很少在文件级优化中应用的优化。
如果一个函数的特定参量的值不变,编译器就会用这个值替换函数中的这个参量。
如果一个函数的返回值从不使用,编译器就会删除该函数的返回代码。
如果一个函数从未被调用,编译器就会删除该函数。
要想察看编译器进行程序级优化的情况,可以使用–on2选项产生一个优化信息文件。
要想控制程序级优化,可以使用–op选项:
–op0:有被其它模块调用的函数和在其它模块中编辑的全局变量。
–op1:没有被其它模块调用的函数,有在其它模块中编辑的全局变量。
–op2:没有被其它模块调用的函数,也没有在其它模块中编辑的全局变量,为缺省值。
–op3:有被其它模块调用的函数,没有在其它模块中编辑的全局变量。
2 嵌入函数(Inline Function)
当程序调用一个嵌入函数时,会把该函数的代码插入到调用处。
嵌入函数有助于提高代码的运行效率,主要有以下两个优点:
省去了函数调用有关的操作;
优化器可以把嵌入函数代码和周围代码放在一起自由地进行优化。
但是,嵌入函数会大幅度地增加程序代码长度,适合于小函数和调用次数较少的场合。
嵌入函数有以下方法:嵌入本征函数、自动嵌入小函数、利用inline关键字嵌入函数。
1. 嵌入本征函数
C55x有很多本征函数。编译时,编译器会用有效代码取代本征函数。
无论是否使用优化器,这种嵌入操作都会自动进行。
2. 自动嵌入小函数
通过–o3选项,优化器将自动地嵌入所调用的小函数。小函数长度的上限由-oi <size>选项指定,即任何长度超过size的函数不能被自动嵌入。如果选择了-oi 0,则等价于取消自动嵌入。
函数大小以编译器内部的绝对单位为准进行计算,用-onx选项可以看到某函数大小。
3. 利用inline关键字嵌入函数
如果inline关键字对函数进行限定,则该函数内调用时将被嵌入到调用处,而不是采用普通的函数调用操作方式。
为使inline关键字生效,必须采用–o(–o0,–o1,–o2 或–o3)选项。–pi选项用于关闭基于inline关键字的函数嵌入。
1.生成高效循环代码
通过改进C循环代码可以极大地提高代码的性能:
避免循环体内的函数调用。这使得编译器可以高效地使用硬件循环结构(repeat、localrepeat和blockrepeat)。
保持小循环代码来使编译器使用localrepeat。
分析往返计数 (trip count) 问题,使用MUST-ITERATE pragma。(The trip count is the number of times a loop iterates.)
使用-o3和-pm编译器选项。
2.高效地使用MAC硬件
C55x有专门的硬件高效执行MAC运算。一个周期中可以执行一个单乘加或一个双乘加(dual-MAC)运算。
用单乘加操作写高效的小循环
产生双乘加操作
3.使用本征(intrinsics)函数
C55x提供了一种特殊函数——本征(intrinsics)函数,使用它可以迅速优化C代码。
本征函数前有个下划线“_”,调用方法和普通函数相同。
使用本征函数可以减少编代码量和系统开销,但会降低代码的可移植性,为此可以用ETSI(European Telecommunications Standards Institute)函数代替本征函数。
对于C55x代码,gsm.h使用编译器本征函数定义了ETSI函数。
ETSI函数可以在主机或其它没有本征函数的目标系统中编译时使用。
4.对16位数使用长整型访问
在某些情况下,把16位数据作为long类型进行访问可以显著提高效率,例如将数据从一个存储器地址快速地传递到另一个存储器地址。由于32位访问也可以在单周期中出现,这样可以减少一半的数据移动时间
唯一的限制在于数据必须排在偶字边界上。如果传递的数据是2的倍数,则代码简单很多,可以用DATA ALIGN pragma来排列数据:
short x[10];
#pragma DATA_ALIGN(x,2)
5.生成高效控制代码
如果程序中“case”个数少于8,编译器在执行套用的if–then–else和switch/case结构时会生成相似的结构。因为第一种为真的情况在执行时的分支最少,所以最好将最常出现的情况写在第一个“case”后。当程序中“case”超过8时,编译器会生成一个.switch标号的段。这种情况下,仍然最好将最常执行的代码放在第一个“case”后
在可能情况下最好测试0,因为通常测试0会得到更高效的代码