(3)平方运算
a = pow(a, 2.0);
可以改为:
a = a*a;
说明:在有内置硬件乘法器的单片机中(如51系列),乘法运算比求平方运算快得多,因为浮点数的求平方是通过调用子程序来实现的,在自带硬件乘法器的AVR单片机中,如ATMega163中,乘法运算只需2个时钟周期就可以完成。既使是在没有内置硬件乘法器的AVR单片机中,乘法运算的子程序比平方运算的子程序代码短,执行速度快。
如果是求3次方,如:
a=pow(a,3.0);
更改为:
a = a*a*a;
则效率的改善更明显。
(4)用移位实现乘除法运算
a = a*4;b = b/4;
可以改为:
a = a << 2;b = b >> 2;
通常如果需要乘以或除以2n,都可以用移位的方法代替。在ICCAVR中,如果乘以2n,都可以生成左移的代码,而乘以其它的整数或除以任何数,均调用乘除法子程序。用移位的方法得到代码比调用乘除法子程序生成的代码效率高。实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果,如:
a = a*9
可以改为:
a = (a << 3) + a
采用运算量更小的表达式替换原来的表达式,下面是一个经典例子:
旧代码:
x = w % 8;y = pow(x, 2.0);z = y * 33;for (i = 0; i < MAX; i ++){ h = 14 * i; printf("%d",h);}
新代码:
x = w & 7; /*位操作比求余运算快*/y = x * x; /*乘法比平方运算快*/z = (y << 5) + y; /*位移乘法比乘法快*/for (i = h = 0; i < MAX; i++){ h += 14; /*加法比乘法快*/ printf("%d",h);}
(5)避免不必要的整数除法
整数除法是整数运算中最慢的,所以应该尽可能避免。一种可能减少整数除法的地方是连除,这里除法可以由乘法代替。这个替换的副作用是有可能在算乘积时会溢出,所以只能在一定范围的除法中使用。
不好的代码:
int i, j, k, m;m = i / j / k;
推荐的代码:
int i,j,k,m;m = i / (j * k);
(6)使用增量和减量操作符
在使用到加一和减一操作时尽量使用增量和减量操作符,因为增量符语句比赋值语句更快,原因在于对大多数CPU来说,对内存字的增、减量操作不必明显地使用取内存和写内存的指令,比如下面这条语句:
x = x+1;
模仿大多数微机汇编语言为例,产生的代码类似于:
move A,x ;把x从内存取出存入累加器Aadd A,1 ;累加器A加1store x ;把新值存回x
如果使用增量操作符,生成的代码如下:
incr x ;x加1
显然,不用取指令和存指令,增、减量操作执行的速度加快,同时长度也缩短了。
(7)使用复合赋值表达式
复合赋值表达式(如a-=1及a+=1等)都能够生成高质量的程序代码。
(8)提取公共的子表达式
在某些情况下,C++编译器不能从浮点表达式中提出公共的子表达式,因为这意味着相当于对表达式重新排序。需要特别指出的是,编译器在提取公共子表达式前不能按照代数的等价关系重新安排表达式。这时,程序员要手动地提出公共的子表达式(在VC.NET里有一项“全局优化”选项可以完成此工作,但效果就不得而知了)。 不好的代码:
float a,b,c,d,e,f;// 。。。e = b * c / d;f = b / d * a;
推荐的代码:
float a,b,c,d,e,f;// 。。。const float t(b / d);e = c * t;f = a * t;
不好的代码:
float a,b,c,e,f;// 。。。e = a / c;f = b / c;
推荐的代码:
float a,b,c,e,f;// 。。。const float t(1.0f / c);e = a * t;f = b * t;
3 结构体成员的布局 很多编译器有“使结构体字,双字或四字对齐”的选项。但是,还是需要改善结构体成员的对齐,有些编译器可能分配给结构体成员空间的顺序与他们声明的不同。但是,有些编译器并不提供这些功能,或者效果不好。所以,要在付出最少代价的情况下实现最好的结构体和结构体成员对齐,建议采取下列方法:
(1)按数据类型的长度排序
把结构体的成员按照它们的类型长度排序,声明成员时把长的类型放在短的前面。编译器要求把长型数据类型存放在偶数地址边界。在申明一个复杂的数据类型 (既有多字节数据又有单字节数据) 时,应该首先存放多字节数据,然后再存放单字节数据,这样可以避免内存的空洞。编译器自动地把结构的实例对齐在内存的偶数边界。
|