您的位置:首页 > 编程语言 > C语言/C++

C optimization tutorial 翻译 C语言优化教程(二)

2013-08-15 13:01 302 查看
from:http://just-study.blogbus.com/logs/37238535.html
6.更快的for循环
一般来时,你会按照下面的方式来编写一个简单的for循环:

for( i=0; i<10; i++){ ... }

i循环计数变量取值依次为:0,1,2,3,4,5,6,7,8,9

如果你的程序与循环计数器改变的顺序无关的话,你可以用下面的语句替代:

for( i=10; i--; ) { ... }

使用上面的代码,循环计数变量i的取值为9,8,7,6,5,4,3,2,1,0,循环执行速度应该会更快些.
这种方式能提高效率是因为i--作为判断条件能够得到更快的处理.i是非零?是的话,递减,继续循环.
而对于原始代码(第一种方式),处理器必须首先进行10与i进行相减,然后判断结果是否为非零,是的话,递增,继续循环(比第二种情况多一条相减运算语句).在一些规模小的循环中,这会产生很大的区别.
第二种语法看起来有点奇怪,但是这是合法的.循环中的第3个语句是可选的(无限循环可以写成"for( ; ; )").下面的代码能产生同样的效果:

for(i=10; i; i--){}

或者进一步扩展为:

for(i=10; i!=0; i--){}

唯一需要时刻注意的是循环终止条件是计数器值最终为0(如果你的循环计数变量值是从80递减50,这种方式是不会生效的),并且循环计数器是递减的.如果代码依赖于递增循环,这种方式也是不会起作用的.

7.使用switch取代if...else...
对于有很多设计到大量if...else...else...判断条件的语句块,就像下面的语句一样:

if( val == 1)
dostuff1();
elseif (val == 2)
dostuff2();
elseif (val == 3)
  dostuff3();

使用switch的方式可能执行更快:

switch( val )
{
case1: dostuff1(); break;

case2: dostuff2(); break;

case3: dostuff3(); break;
}

在if语句块中,如果最后一种情况需要执行,所有的先前条件都会测试一次.而switch帮我们节省了这些额外的工作.如果你必须考虑使用很大的if...else...判断语句块,考虑把最可能发生的情况放在最前面.

8.指针
无论什么时候,通过引用的方式传结构体(例如,传递结构体指针),否则的话,整个结构体内容将要拷贝到栈中作为值传递,这样将会放慢执行速度.(我曾经见过有程序员把好几Mb的结构体内容的按值传递给调用函数,然而,事实上只需要通过一个简单的指针就能做同样一件事情.)
如果接受结构体指针参数的函数体不会改变结构体的内容,那么应该把该参数申明为指向const内容的指针.

void print_data( const bigstruct *data_pointer)
{
...printf contents of structure...
}

这个示例告诉编译器函数体不改变外部结构体的内容,不需要在每次访问结构体变量的时候重读结构体内容.同时,这样也可以防止你的代码意外的修改只读结构体内容.

9.提前退出循环
  完整地执行循环体常常是一种多次一举,反而会降低执行效率.例如,如果你在一个大数组中查找一个特殊的数据项,一旦你找到你需要的数据项,尽快退出循环吧.
  例如:
  下面的语句用于在一个大小为10000个数组中看是否有-99这个数.

found = FALSE;
for(i=0;i<10000;i++)
{
if( list[i] == -99 )
{
found = TRUE;
}
}
if( found ) printf("Yes, there is a -99. Hooray!\n");

这种方式能够得到想要的结果,但是不管查找到的数据在数组的位置而执行了整个for循环.

found = FALSE;
for(i=0; i<10000; i++)
{
if( list[i] == -99 )
{
found = TRUE;
break;
}
}
if( found ) printf("Yes, there is a -99. Hooray!\n");

如果我们要查找的-99这个数据在数组的第23号位置,循环应该在第23次循环的时候终止,而不去执行剩下的9977次迭代.

10 杂项
1 一般来说,可以通过增加内存的使用来提高程序执行速度.例如,如果你能够缓存经常需要的数据而不是重复地计算或者加载它,这种方法就能生效.这样的例子很多,例如sine/cosine 表,伪随机数表(在开始的时候一次性计算1000次,然后重用它)

2 避免在循环表达式中使用++或者--等之类有二义性的语句.例如while(n--){};这种方式很难被编译器优化.

3 尽可能少的使用全局变量.

4 声明文件内的函数或者变量等为static,除非你打算将它用于全局范围内.

5 如果可以的话使用word-size大小的变量,因为处理器能更好的处理这些数据(而不是char,short,double,bitfields等数据类型)

6 不要使用递归.递归函数看起来很华丽和紧凑,但是它会使用很多函数调用,而这些函数调用将占据很大的开销.

7 避免在循环中使用sqrt函数求平方根---计算平方根很耗CPU.计算量大,函数执行周期长.

8 单维数组比多为数组执行速度更快.

9 编译器常常能优化整个文件---避免分隔一些相关性高的函数到不同的文件中.如果编译器能发现两个相关性高的单元在一块,优化的机会更大(例如,它可能把代码设置为内联).

10 单精度数学运算比双精度运算速度更快---通常编译器有针对于这个的一个开关.

11 浮点型数据乘法运算常常比除法运算更快---推荐使用val*0.5替代val/2.0

12 加法运算比乘法运算要快---推荐使用val+val+val替代val*3.

13 puts函数虽然灵活性差点,但是性能比printf函数要高.

14 使用#define宏来替代一些通用的小函数.---有时候,在一个规模小的循环中,大量CPU的使用被转移到成千上万次小的外部函数的调用中.使用宏替代函数来执行相同的工作将节省所有函数的调用开销,这样做同时还能使编译器更进一步优化.

15 二进制/无格式文件的访问比文本文件的访问要块.因为机器没必要在人易读的ASCII和机器易读的二进制之间进行转换.如果你实际上不需要读文件的数据,考虑把它写成二进制文件.

16 如果你的库支持mallopt()函数,使用它吧.MAXFAST设置能够使代码执行效率有显著的提高.如果一个特殊的结构体在短时间内反复创建和销毁,设置mallopt选项使它能够效率更好.

17 最后一点,但不是最不重要的一点,打开编译器优化.因为等到最后总是急冲冲将产品及时交给客户,这样做虽然显而易见,但是经常被遗忘.相对于在源码级执行的优化,编译器在更低一级能更进一步优化,并且能够针对目标处理器执行更具体的优化.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐