C语言程序运行速度优化方法谈.docx
《C语言程序运行速度优化方法谈.docx》由会员分享,可在线阅读,更多相关《C语言程序运行速度优化方法谈.docx(18页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、C语言程序运行速度优化方法谈1、选择合适的算法和数据结构选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删 除指令,那使用链表要快得多。数组与指针语句具有十分密切的关系,一般来说,指针比拟 灵活简洁,而数组那么比拟直观,容易理解。对于大局部的编译器,使用指针比使用数组生成 的代码更短,执行效率更高。在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。 与数组索弓I相比,指针一般能使代码速度更快,占用空间更少。使用多维数组时差异更明显。 下面的代码作用是相同的,但是效率不一样。数组索引 指针运算For(;)A=arrayt+;For(;)A=arr
2、ayt+;p=arrayfor(;) a=*(p+);指针方法的优点是,array的地址每次装入地址p后,在每次循环中只需对p增量操作。 在数组索引方法中,每次循环中都必须根据t值求数组下标的复杂运算。2、使用尽量小的数据类型能够使用字符型(char)定义的变量,就不要使用整型(int)变量来定义;能够使用整型变 量定义的变量就不要用长整型(long int),能不使用浮点型(float)变量就不要使用浮点型变量。 当然,在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C编译器并不报 错,但程序运行结果却错了,而且这样的错误很难发现。在ICCAVR中,可以在Options中设定使用
3、printf参数,尽量使用基本型参数(%c、d、x、X、11和$格式说明符),少用长整型参数(出、lu、以和区格式 说明符),至于浮点型的参数(。那么尽量不要使用,其它C编译器也一样。在其它条件不变 的情况下,使用f参数,会使生成的代码的数量增加很多,执行速度降低。3、减少运算的强度(1)、查表(游戏程序员必修课)一个聪明的游戏大虾,基本上不会在自己的主循环里搞什么运算工作,绝对是先计算 好了,再到循环里查表。看下面的例子:旧代码:long factorial(int i)if (i = 0)return 1;do_stuff(i); i+;do_stuff(i); i+;do_stuff(i
4、); i+;do_stuff(i); i+;do_stuff(i); i+;do_stuff(i); i+;do_stuff(i); i+;do_stuff(i); i+;do_stuff(i); i+;)可以看出,新代码里比拟指令由100次降低为10次,循环时间节约了 90%。不过注 意:对于中间变量或结果被更改的循环,编译程序往往拒绝展开,(怕担责任呗),这时候就需 要你自己来做展开工作了。还有一点请注意,在有内部指令cache的CPU上(如MMX芯片),因为循环展开的代 码很大,往往cache溢出,这时展开的代码会频繁地在CPU的cache和内存之间调来调去, 又因为cache速度很高,
5、所以此时循环展开反而会变慢。还有就是循环展开会影响矢量运算 优化。(6)、循环嵌套把相关循环放到一个循环里,也会加快速度。旧代码:for (i = 0; i MAX; i+)/* initialize 2d array to Os */for (j = 0; j MAX; j+)aiU = 0.0;fbr (i = 0; i MAX; i+)/* put Ts along the diagonal */aii = 1.0;新代码:fbr (i = 0; i MAX; i+)/* initialize 2d array to Os */for (j = 0; j type)case FREQUE
6、NT_MSG1:handleFrequentMsg();break;case FREQUENT MSG2:handleFrequentMsg2();break;case FREQUENT_MSGn:handleFrequentMsgnQ;break;default:嵌套局部用来处理不经常发生的消息switch (pMsg-type) (case INFREQUENT MSGl:handlelnfrequentMsg 1();break;case INFREQUENT_MSG2:handleInfrequentMsg2();break; o o o o o ocase INFREQUENT_MS
7、Gm:handleInfrequentMsgm();break;如果switch中每一种情况下都有很多的工作要做,那么把整个switch语句用一个指向 函数指针的表来替换会更加有效,比方下面的switch语句,有三种情况:enum MsgTypeMsgl, Msg2, Msg3switch (ReceiveMessage()(case Msgl;o o o o o ocase Msg2;case Msg3;)为了提高执行速度,用下面这段代码来替换这个上面的switch语句。/*准备工作*/int handleMsg 1 (void);int handleMsg2(void);int handl
8、eMsg3(void);/*创立一个函数指针数组*/int (*MsgFunction )()= handleMsg 1, handleMsg2, handleMsg3;/*用下面这行更有效的代码来替换switch语句*/status=MsgFunctionReceiveMessage()();(9)、循环转置有些机器对JNZ(为0转移)有特别的指令处理,速度非常快,如果你的循环对方向不敏 感,可以由大向小循环。旧代码:for (i = 1; i = MAX; i+)(o o o)新代码:i = MAX+l;while (i)(o o o不过千万注意,如果指针操作使用了 i值,这种方法可能引起
9、指针越界的严重错误(i = MAX+l;)o当然你可以通过对i做加减运算来纠正,但是这样就起不到加速的作用,除非类 似于以下情况:旧代码:char aMAX+5;fbr (i = 1; i = MAX; i+)*(a+i+4 尸 0;新代码:i = MAX+l;while (-i)*(a+i+4)=0;(10)、公用代码块一些公用处理模块,为了满足各种不同的调用需要,往往在内部采用了大量的 if-then-else结构,这样很不好,判断语句如果太复杂,会消耗大量的时间的,应该尽量减少 公用代码块的使用。(任何情况下,空间优化和时间优化都是对立的-东楼)。当然,如果仅 仅是一个(3=x)之类的简
10、单判断,适当使用一下,也还是允许的。记住,优化永远是追求一 种平衡,而不是走极端。(11)提升循环的性能要提升循环的性能,减少多余的常量计算非常有用(比方,不随循环变化的计算)。 不好的代码(在for()中包含不变的if():fbr( i o o o )if( CONSTANTO )(DoWorkO( i); /假设这里不改变CONSTANTO的值)else(DoWorkl(i); /假设这里不改变CONSTANTO的值)推荐的代码:iR CONSTANTO)fbr( i o o o )(DoWorkO( i );elsefbr( i o o o )jDoWorkl(i);)如果已经知道if(
11、)的值,这样可以防止重复计算。虽然不好的代码中的分支可以简单地预测, 但是由于推荐的代码在进入循环前分支已经确定,就可以减少对分支预测的依赖。(12)、选择好的无限循环在编程中,我们常常需要用到无限循环,常用的两种方法是while (1)和for (; ; )o这 两种方法效果完全一样,但那一种更好呢?然我们看看它们编译后的代码:编译前:while (1); 编译后:mov eax, 1 test eax, eax je foo+23h jmp foo+18h 编译前:for (;); 编译后:jmp fbo+23h显然,for(;)指令少,不占用寄存器,而且没有判断、跳转,比while (1
12、)好。6、提高CPU的并行性(1)使用并行代码尽可能把长的有依赖的代码链分解成几个可以在流水线执行单元中并行执行的没有依 赖的代码链。很多高级语言,包括C+,并不对产生的浮点表达式重新排序,因为那是一个 相当复杂的过程。需要注意的是,重排序的代码和原来的代码在代码上一致并不等价于计算 结果一致,因为浮点操作缺乏精确度。在一些情况下,这些优化可能导致意料之外的结果。 幸运的是,在大局部情况下,最后结果可能只有最不重要的位(即最低位)是错误的。 不好的代码:double a100, sum;int i;sum = O.Of;for (i=0; i100; i+)sum += ai;推荐的代码:do
13、uble a100, suml, sum2, sum3, sum4, sum;int i;suml = sum2 = sum3 = sum4 = 0.0;for (i = 0; i 100; i += 4)(suml += ai;sum2 += ai+l;sum3 += ai+2;sum4 += ai+3;)sum = (sum4+sum3)+(sum 1 +sum2);要注意的是:使用4路分解是因为这样使用了4段流水线浮点加法,浮点加法的每一 个段占用一个时钟周期,保证了最大的资源利用率。(2)防止没有必要的读写依赖当数据保存到内存时存在读写依赖,即数据必须在正确写入后才能再次读取。虽然 A
14、MD Athlon等CPU有加速读写依赖延迟的硬件,允许在要保存的数据被写入内存前读取 出来,但是,如果防止了读写依赖并把数据保存在内部寄存器中,速度会更快。在一段很长 的又互相依赖的代码链中,防止读写依赖显得尤其重要。如果读写依赖发生在操作数组时, 许多编译器不能自动优化代码以防止读写依赖。所以推荐程序员手动去消除读写依赖,举例 来说,引进一个可以保存在寄存器中的临时变量。这样可以有很大的性能提升。下面一段代 码是一个例子:不好的代码:float xVECLEN, yVECLEN, zVECLEN;o o o o o ofbr (unsigned int k = 1 ; k VECLEN ;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 程序 运行 速度 优化 方法
限制150内