




已阅读5页,还剩4页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
优化-c程序员之终极标靶一个用户往往把他的生命中大部分时间用来等待计算机输出结果,为了减少这个等待时间,用户不得不采购更快的计算机,增加内存或更换整个网络.开发者有责任尽量避免他的程序耗费昂贵的资源,为用户挽回宝贵的时间和金钱.-原作者请参考:优化-c程序员之终极标靶-介绍:最简单的优化方法是借助prof工具判断程序的瓶颈在哪里,你必须判断出程序的那些部分消耗了大量资源.一旦你判断出瓶颈(比如说执行上万次的循环),你所做的第一件事就是重新设计程序,减低循环次数.当然,现在绝大多数优化编译器可以做到这一点,(不过最好还是自己来-东楼),但是记住,当以下情况出现时,优化是在浪费时间:1)程序只写了一部分2)程序还没有测试通过3)看起来已经足够快了还要注意的就是判断程序的用途,如果仅仅为了得到一份报告而写的仅运行一次的程序,用户往往在午餐前运行程序,这时,程序只要在他们回来之前运行完就可以了,如果程序调用其他的程序,而且其他程序都比较慢,那么优不优化效果都差不多,但是,如果是gui图形用户界面程序(比如鼠标光标显示程序),那么一点点的延迟都会遭到用户的投诉完成优化后,带上所有的优化命令编译,然后用你实际使用的数据测试它,如果做不到这一点,请小心选择你的测试数据,程序员多半倾向于按照程序的要求给输入数据,但用户可不这么干.如果你已经完成了所有优化,但是程序仍然看起来不快,注意一下你的操作系统,很多多任务操作系统按时间片来划分用户资源,如果给你的资源太少,那和你的系统管理员联系吧.1.选择一个更好的算法:应该熟悉算法语言,知道各种算法的优缺点,一般很多计算机资料文本上有介绍,应该能够看得懂算法描述.这里是一些明显可以通用的替换慢的算法替换成顺序查找二分法查找或乱序查找、插入排序或冒泡排序快速排序,合并排序,根(radix)排序还要选择一种合适的数据结构(记着,你的程序所干的唯一一件事就是在计算机里搬数,把一堆数从一个地方提出来,处理一下,甩到另一个地方,那么按什么方式搬数有多重要你应该知道了吧-东楼),比如你在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多.如果你要做二分法查找,那提前排下序非常重要.2.写一些清晰,可读性好并且简单的代码一个人容易看得懂的程序同样也容易被编译器读懂.一个大而复杂的表达式往往会把编译器脑袋都弄大,为了防止自己发疯,编译器往往放弃对这段代码的优化.但绝对不会向你报告,出于维护自己面子起见,东楼发现所有的编译器都只会向你报告它优化了多少,而决不会报告它干不了的有多少,东楼就亲眼见到一个瓜编译器因为一个表达式弄昏了头,把整个模块的优化都放弃了,回来居然还恬不知耻的报告优化非常顺利,整个儿一个报喜不报忧.适当的时候尽量减小每个函数的代码量(这时候对代码要抠一点,懂吗?-东楼),不过也别走极端,别为了优化把一个函数写成10页纸的一堆函数,那编译器倒高兴了,可人发疯了.优化后,赶快找一台快点的机器看看效果吧(满足一下虚荣心,嬉嘻!)3.透视你的程序一个程序写出来,凭直觉就应该感觉出哪些地方快,哪些地方慢,(就是,东楼的程序就是全部凭直觉优化的(.反正吹牛不上税,嘻嘻),一般说来,最快的运算就是分配一块内存,给指针赋值,还有就是两个整数的加法运算,别的都有点慢,最慢的就要数打开文件啦,打开新的进程啦,读写一大块内存啦,寻找啦,排序啦等等,别看这帮虾子指令都只要几个微秒,可成百上千的杀将过来,东楼可受不了.一定不能让这帮虾子进循环,干了它.这是经常犯的一个错误:if(x!=0)x=0;程序的原意是当x等于0时,节约时间不执行赋值操作,可你别忘了,赋值语句才是最快的,那还不如直接写成下面的语句更来劲.x=0;还有就是一些神勇的大虾,非得等到编译器把代码输出成汇编语言级然后拿着计算器一行行加汇编指令的个数和周期数,才算优化完成了,不过可别忘了,最后一次优化不是obj代码级的,而是由link程序完成的,这没多大用.4.理解你的编译程序选项许多编译程序有几级优化选项,注意使用最优化的一项,特别注意gcc,优化选项非常多,小心使用,别弄得适得其反.通常情况下一旦选用最高级优化,编译程序会近乎病态地追求代码优化,精简指令,(如djgpp的-o3),但少数情况下,这会影响程序的正确性,这是你只有改自己的程序啦.不过也有这种情况,就是一些参数会影响优化的程序,但是不会影响普通程序,这时只有具体情况具体分析了.5.内联(内嵌)gcc(使用-finline-functions参数),还有一些别的编译器可以在最高级优化中内联一些小的函数.k&c编译器则只有在库函数是用汇编写成的时候才内联,c+编译器普遍支持内联函数.不过把c函数写成宏也能达到加速的作用,不过必须是在程序完全除错之后,因为绝大多数除错程序不支持宏除错.宏内联的例子:旧代码:int foo(a,b)a=a-b;b+;a=a*b;returna;新代码:#definefoo(a,b)(a)-(b)*(b)+1)注意最外层括号是必须的,因为当宏在表达式中展开时,你不知道表达式里还有没有比乘法级别更高的运算.一些警告:1.无限制地使用宏可以使代码爆炸,程序会很快消耗完你所有的资源,包括物理内存,最后系统要么崩溃,要么把你的代码放到虚拟内存(磁盘上)中去,那你再怎么优化也没用了2.c的宏每次调用都要对参数赋值,如果参数很多很复杂,那光赋值就要消耗大量的cpu时间,效果还不如不用宏3.因为宏允许包含很复杂的表达式,所以编译程序会非常辛苦,为了使自己不至于完全发疯,一般编译程序对宏能包含的字符数都有一个限制,注意别犯规.4.一旦用了宏,prof程序也跟着糊涂起来了,这是它说的话可信度可不高6.循环展开这是经典的速度优化,但许多编译程序(如gcc-funroll-loops)能自动完成这个事,所以现在你自己来优化这个显得效果不明显.(这里说一句,云风工作室的云风朋友曾来信和东楼专门探讨过这个问题,他根据自己在djgpp的经验认定循环展开无效,东楼猜测可能就是因为gcc在编译时自动进行了展开,所以手工展开已经没多大效果了.但这个方法总是对的).旧代码:for(i=0;i100;i+)do_stuff(i);新代码:for(i=0;i100;)do_stuff(i);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+;do_stuff(i);i+;do_stuff(i);i+;do_stuff(i);i+;可以看出,新代码里比较指令由100次降低为10次,循环时间节约了90%.不过注意:对于中间变量或结果被更改的循环,编译程序往往拒绝展开,(怕担责任呗),这时候就需要你自己来做展开工作了.还有一点请注意,在有内部指令cache的cpu上(如mmx芯片),因为循环展开的代码很大,往往cache溢出,这时展开的代码会频繁地在cpu的cache和内存之间调来调去,又因为cache速度很高,所以此时循环展开反而会变慢.还有就是循环展开会影响矢量运算优化.7.循环嵌套把相关循环放到一个循环里,也会加快速度.旧代码:for(i=0;imax;i+)/*initialize2darrayto0s*/for(j=0;jmax;j+)aij=0.0;for(i=0;imax;i+)/*put1salongthediagonal*/aii=1.0;新代码:for(i=0;imax;i+)/*initialize2darrayto0s*/for(j=0;jmax;j+)aij=0.0;aii=1.0;/*put1salongthediagonal*/8.循环转置有些机器对jnz(为0转移)有特别的指令处理,速度非常快,如果你的循环对方向不敏感,可以由大向小循环旧代码:for(i=1;i=max;i+).新代码:i=max+1;while(-i).不过千万注意,如果指针操作使用了i值,这种方法可能引起指针索引超界的严重错误(i=max+1;).当然你可以通过对i做加减运算来纠正,但是这样加速的作用就没有了除非类似于以下情况旧代码:char amax+5;for(i=1;i=max;i+)*(a+i+5)=0;新代码:i=max+1;while(-i)*(a+i+4)=0;9.减小运算强度采用运算量更小的表达式替换原来的表达式,下面是一个经典例子:旧代码:x=w%8;y=pow(x,2.0);z=y*33;for(i=0;imax;i+)h=14*i;printf(%d,h);新代码:x=w&7;/*位操作比求余运算快*/y=x*x;/*乘法比平方运算快*/z=(y5)+y;/*位移乘法比乘法快*/for(i=h=0;ib-c4-aardvark+a-b-c4-baboon+a-b-c4-cheetah+a-b-c4-dog;新代码:structanimals*temp=a-b-c4;total=temp-aardvark+temp-baboon+temp-cheetah+temp-dog;一些老的c语言编译器不做聚合优化,而符合ansi规范的新的编译器可以自动完成这个优化,看例子:floata,b,c,d,f,g;.a=b/c*d;f=b*g/c;这种写法当然要得,但是没有优化floata,b,c,d,f,g;.a=b/c*d;f=b/c*g;如果这么写的话,一个符合ansi规范的新的编译器可以只计算b/c一次,然后将结果代入第二个式子,节约了一次除法运算.11.公用代码块一些公用处理模块,为了满足各种不同的调用需要,往往在内部采用了大量的if-then-else结构,这样很不好,判断语句如果太复杂,会消耗大量的时间的,应该尽量减少公用代码块的使用.(任何情况下,空间优化和时间优化都是对立的-东楼).当然,如果仅仅是一个(3=x)之类的简单判断,适当使用一下,也还是允许的.记住,优化永远是追求一种平衡,而不是走极端.12.采用递归与lisp之类的语言不同,c语言一开始就病态地喜欢用重复代码循环,许多c程序员(包括东楼)都是除非算法要求,坚决不用递归.事实上,c编译器们对优化递归调用一点都不反感,相反,它们还很喜欢干这件事.只有在递归函数需要传递大量参数,可能造成瓶颈的时候,才应该使用循环代码,其他时候,还是用递归好些.13.查表(游戏程序员必修课)一个聪明的游戏大虾,基本上不会在自己的主循环里搞什么运算工作,绝对是先计算好了,再到循环里查表.(东楼每一次写游戏,基本上都有一大堆表格).看下面的例子:旧代码:long factorial(int i)if(i=0)return1;elsereturn i*factorial(i-1);新代码:static long factorial_table=1,1,2,6,24,120,720/*etc*/;long factorial(inti)return factorial_tablei;如果表很大,不好写,就写一个init函数,在循环外临时生成表格.14.变量在最内层循环避免使用全局变量和静态变量,除非你能确定它在循环周期中不会动态变化,大多数编译器们优化变量仅有置成寄存器变量一招,而对于动态变量,它们干脆放弃对整个表达式的优化.尽量避免把一个变量地址传递给另一个函数,虽然这个还很常用.c语言的编译器们总是先假定每一个函数的变量都是内部变量,这是由它的机制决定的,在这种情况下,它们的优
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年行政执法资格证考试题库及答案
- 2025年乡村振兴战略技能知识考试题与答案
- 2025生殖学试题及答案
- 南召辅警考试题库2025(有答案)
- 2025建筑材料采购租赁合同范本
- 2025原材料采购销售合同示范文本
- 出口退税专项课件
- 2025设备采购与销售合同范本
- 多重耐药菌的监测与控制2讲课文档
- 2025年度个人借款抵押合同
- 2025年内科慢性疾病治疗路径分析测试答案及解析
- 2025年语言能力等级考试英语模拟试题及参考答案全集
- 2025全国小学生“学宪法、讲宪法”活动知识竞赛题库及答案
- 2025-2026学年北师大版小学数学四年级上册教学计划及进度表
- 客运驾驶员安全行车课件
- 国防知识教育培训课件
- 湖南省长沙市华益中学2024-2025学年九年级上学期开学测试语文试题(答案)
- 医院内肺炎预防与控制操作规程
- 神经外科手术机器人辅助脑干出血穿刺引流术专家共识解读
- 2025年吴忠市公安局招聘警务辅助人员招聘考试笔试试题(含答案)
- 2025年专业医疗机构感染控制与消毒作业外包服务合同书
评论
0/150
提交评论