实时程序设计中代码的优化

在嵌入式的系统开发中,出于对低价产品的需求, 硬件的设计者需要提供刚好足够的存储器和完成工作的处理能力。所以在嵌入式软件设计的最后一个阶段则变成了对代码的优化。
代码优化的目标是体积小和速度快,可以从算法、数据和指令流三方面来考虑。
3.1 算法优化
大多数情况下,速度同内存(或者是性能,比如说压缩性能)是不可兼得的。目前程序加速的常用算法一个大方面就是利用查表来避免计算(比如在jpg有huffman码表,在YUV到RGB变换也有变换表)这样原来的复杂计算现在仅仅查表就可以了,虽然浪费了内存,不过速度显著提升。此外在编写程序时还要注意提高效率,例如:
3.1.1Switch语句中根据发生频率来进行case排序
switch语句是一个普通的编程技术,编译器会产生if-else-if的嵌套代码,并按照顺序进行比较,发现匹配时,就跳转到满足条件的语句执行。使用时需要注意。每一个由机器语言实现的测试和跳转仅仅是为了决定下一步要做什么,就把宝贵的处理器时间耗尽。为了提高速度,设法根据具体的情况按照它们发生的相对频率排序。换句话说,把最可能发生的情况放在第一位,最不可能的情况放在最后。
3.1.2将大的switch语句转为嵌套switch语句
当switch语句中的case标号很多时,为了减少比较的次数,明智的做法是把大switch语句转为嵌套switch语句。把发生频率高的case 标号放在一个switch语句中,并且是嵌套switch语句的最外层,发生相对频率相对低的case标号放在另一个switch语句中。 如果switch中每一种情况下都有很多的工作要做,那么把整个switch语句用一个指向函数指针的表来替换会更加有效。
3.1.3用指针代替数组
在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。与数组索引相比,指针一般能使代码速度更快,占用空间更少。使用多维数组时差异更明显。下面的代码作用是相同的,但是效率不一样。
数组索引 指针运算
For(;;){ p=array
A=array[r++]; for(;;){
a=*(p++);
...... ......
} }
指针方法的优点是,array的地址每次装入地址p后,在每次循环中只需对p增量操作。在数组索引方法中,每次循环中都必须进行基于r值求数组下标的复杂运算。
3.1.4使用宏函数而不是函数。例如:
#define bwMCDR2_ADDRESS 4
#define bsMCDR2_ADDRESS 17
#define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)
#define BIT_MASK(__bf) (((1U << (bw ## __bf)) - 1) << (bs ## __bf))
#define SET_BITS(__dst, __bf, __val) ((__dst) = ((__dst) & ~(BIT_MASK(__bf))) | (((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))
SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);
函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。
3.2 Data optimization数据优化
比算法优化层低一级的是数据优化层,我们可以通过改变算法使用的数据类型来优化算法。主要的目的是使处理的数据和目标结构的特性相一致。这项优化不需要大量的代码重写,并独立于算法优化的执行而执行.例如:
3.2.1确定浮点型变量和表达式是 float 型
为了让编译器产生更好的代码,必须确定浮点型变量和表达式是 float 型的。要特别注意的是,以 ";F"; 或 ";f"; 为后缀(比如:2.718f)的浮点常量才是 float 型,否则默认是 double 型。为了避免 float 型参数自动转化为 double,请在函数声明时使用 float。
3.2.2使用32位的数据类型
编译器有很多种,但它们都包含的典型的32位类型是:int,signed,signed int,unsigned,unsigned int,long,signed long,long int,signed long int,unsigned long,unsigned long int。尽量使用32位的数据类型,因为它们比16位的数据甚至8位的数据更有效率。
3.2.3明智使用有符号整型变量
在很多情况下,你需要考虑整型变量是有符号还是无符号类型的。在许多地方,考虑是否使用有符号的变量是必要的。在一些情况下,有符号的运算比较快;但在一些情况下却相反。 比如:整型到浮点转化时,使用大于16位的有符号整型比较快。因为x86构架中提供了从有符号整型转化到浮点型的指令,但没有提供从无符号整型转化到浮点的指令。在整数运算中计算商和余数时,使用无符号类型比较快。
3.3 Instruction flow optimization指令流优化
第三层优化的目标是低级指令流。比较常见的技术是循环合并(loop merging),循环展开(unrolling),软件流水(software pipelining)。
3.3.1循环合并
如果两个循环计数差不多、循环执行互不相同的操作,可以把它们合并在一起组成一个循环。当两个循环的负荷都不满时,这是非常有用的。
3.3.2循环展开
循环展开就是把循环计数小的循环展开,成为非循环形式的串行程序,或者把循环计数大的循环部分展开,减少循环迭代次数,这样可以节省了用于循环设置、初始化、增加和校对循环计数器的时间。大多数编译器可以自动完成这项工作,手工编译会出现错误代码。
例如:
for( int i = 0; i < 3; i++ ) array[i] = i;
逻辑上等同于:
array[0] = 0; array[1] = 1, array[2] = 2;
3.3.3软件流水
软件流水是用来安排循环指令,使这个循环多次迭代并行执行的一种技术。在嵌套循环中,编译器仅对最里面的循环执行软件流水,因此对执行周期很少的内循环作循环展开,外循环进行软件流水,这样可以改进C代码并行执行的性能。使用软件流水还应当注意:尽管软件流水循环可以包含内联函数,但是不能包含函数调用;在循环中不可以有条件终止指令;在循环体中不可以修改循环控制变量。

你可能感兴趣的:(优化,算法,float,编译器,DST,optimization)