科研和工程中的C++编程3-代码优化_第1页
科研和工程中的C++编程3-代码优化_第2页
科研和工程中的C++编程3-代码优化_第3页
科研和工程中的C++编程3-代码优化_第4页
科研和工程中的C++编程3-代码优化_第5页
已阅读5页,还剩24页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

科研和工程中的C++编程代码优化浙江大学计算机学院

袁昕代码优化剖析(profile)

利用工具分析程序代码,调用频率,计算时间等。要防止:

(1)优化了错误的代码:若一个程序的主要指标不是效率,那么一切花在使其更高效上的时间都是浪费。靠直觉来判断哪段代码的主要指标是效率是不可信的,只有直接去测量。

(2)程序员经常“优化”到降低了代码的速度。这在C++是一个典型问题,一个简单的指令行可能会产生巨大数量的机器代码,应当经常检查编译器的输出,并且剖析之*。代码优化构造和析构:

未经认真设计的程序经常花费不少时间在调用构造函数,拷贝对象以及初始化临时对象等等。代码优化即便arg为0,也付出了调用Object的构造函数的代价。特别是如果arg经常是0,并且Object本身还分配内存,这种浪费会更加严重。显然的解决方案就是把obj的定义移到判断之后。在循环中定义复杂变量要注意,若在循环中按照除非需要否则不构造的原则构造了复杂的对象,那么在每一次循环的时候都要付出一次构造的代价。最好在循环外构造之,并只构造一次。如果一个函数在内循环中被调用,而该函数在栈内构造了一个对象,那么可以在外部构造并传递一个引用给它。代码优化尽量采用初始化列表,类中的成员类对象,如果在构造方法里初始化,一般是使用了等号操作符,这样就等于是调用了无参数构造方法一次,再调用一次等号操作符。而使用初始化列表则直接调用其拷贝构造方法*。要前自增不要后自增(即要++A不要A++),后自增会产生临时对象,调用构造方法。对于整数,这没有额外的负担,但对于用户自定义类型,这就是浪费。代码优化尽量少使用有返回值的操作符,如Toperator+(constT&)等,它返回时将构造临时对象,调用构造方法。可尝试C++0x引入的新的语法:右值引用。操作符+-*/%等都需要被设计成有返回值的方法,因此方法体内部出错时,只能以抛出异常的方式来通知调用者。代码优化构造方法尽可能写轻量级代码,有些成员初始化可以使用两步的方法,构造方法什么都不作,另外设计一个方法来初始化成员。要注意类型转换会产生隐藏的临时对象,所以要针对各种类型重载操作符方法。代码优化虚函数:

虚函数的机制很简单。为了完成一个对象的虚函数调用,编译器访问对象的虚函数表,获得一个成员函数的指针,设置调用环境,然后跳转到该成员函数的地址上。一个虚函数调用的额外负担是虚函数表的间接指向;由于事先并不知道将要跳转的地址,所以也有可能造成处理器不能命中Cache。代码优化一般的C++程序都对虚函数有大量的使用,所以主要的手段是防止在那些极其重视效率的地方的虚函数调用。可以改成内联函数以省去函数调用的开销,也可以利用模版类的特点使用mix-inclass模式(混同体,设计模式书中是策略模式)。如下图:代码优化代码优化其中的模版参数T是最终派生类。这样在CXXX实例中调用XXX()方法时,将调用最终派生类CXXX中重载的DoXXX()方法,也做到了多态,但省去了虚函数表的调用开销。

Windows下ATL编程还可以在父类上进一步加ATL_NO_VTABLE关键字,如classATL_NO_VTABLECXXXImpl,目的使得父类构造方法不初始化虚函数表,从而优化时把父类虚函数表及虚函数体从最后生成的模块中删除,从而减少了可执行文件的大小,进而减少内存页的切换。代码优化在很小的、频繁使用的类上使用任何虚函数会造成额外的负担,这些都是不能接受的。由于继承一般都要用到一个或几个虚函数(至少有一个虚的析构函数),所以没必要在小而频繁使用的对象上使用任何继承。当然,继承的层次越少越好,一般3~5层足够。代码优化

MFC、Qt、WxWidget、C#、Java等都使用了单根的类继承体系,显然它们都会有上面的问题,而且用单根继承来描述世界有点怪异,使用多继承组合、概念泛化、基于对象的设计来描述世界就比较自然。关于C++类对象的内存布局,参考书有:

《深入探索C++物件模型》代码优化函数/方法参数的设计尽可能使用简单的类型,这样可以减少额外的类方法的调用。若参数类型为类,在隐式类型转换中就会调用构造方法,造成额外的调用负担。如:

voidFoo(std::string&str){…}

Foo(“aaa”);代码优化这里对Foo()的调用包括了对给定constchar*参数的构造函数的调用。在一般的实现中,这个构造函数执行了一个malloc(),一个strlen(),以及一个memcpy()。由于该例子中的string没有被更多的应用,接着就调用了析构函数,即free()。这里的内存分配完全是浪费,因为字符串“aaa”早就在程序的数据段中了,因而有它在内存中的副本。如果Foo参数定义成constchar*,那么就没有了上面所说的那些额外的调用。对应的较好的形式如下:

voidFoo(constchar*str){…}代码优化inline关键字的优化功能使用内联函数/方法可消除函数调用的系统开销,但是内联太多代码可能使应用程序很大,致使虚拟内存页的错误数增加(即内存页交换也要时间开销)。所以要检查每个函数/方法,决定是否内联,以防止生成的代码膨胀。最终运行效率以测量的时间为准(如用性能监视器

perfmon.exe)*。代码优化减少缓存未命中和页错误

缓存未命中不论出现在内部缓存中,还是出现在外部缓存中,都会降低程序的性能;而页错误由于会转到二级存储中获得程序指令和数据,也会降低程序的性能。

为避免此问题,使用具有良好引用地址的数据结构很重要,这意味着应将相关的事物合在一起。有时看上去很棒的数据结构由于引用地址不好而变得很糟,有时正好相反。代码优化

(1)动态分配的链接表可以降低程序性能,当搜索项或者在表中遍历到末尾时,每个跳过的链接都可能未命中缓存或导致页错误。基于简单数组的表实现由于较好的缓存和较少的页错误,实际上可能快得多,即使考虑到数组更难增长的事实,它仍然可能更快。代码优化

(2)使用动态分配的链接表的哈希表可能降低性能。通过扩展,使用动态分配的链接表存储内容的哈希表的性能可能显著降低。事实上,在最后的分析中,通过数组的简单线性搜索实际上可能更快(取决于具体的情况)。基于数组的哈希表(所谓的“关闭散列”)是通常具有极佳的性能但却经常被忽略的实现。代码优化

(3)排序和查找也要注意容器内元素的引用地址和预期其涉及的数据。元素的引用地址连续能提高运行效率。

(4)内存分配:尽量分配连续的内存来使用。如果经常执行小的分配,可使用自定义分配策略,先分配大的内存块,然后使用自定义的Helper函数从该块分配需要的小内存。代码优化阅读ATL和STL中的集合类(array,list,map,hash,red-blackmap)的实现代码,并对比它们使用内存的方式,分析其效率。再和数据结构书上的集合类的实现进行对比,以及对比自己的数据结构实现和C++课程作业,并得到结论。代码优化

(5)较小的工作集(由模块文件,即程序集大小来决定)意味着更好的引用地址、更少的页错误和更多的缓存命中。进程工作集是操作系统为测量引用地址而直接提供了最接近的尺度。

动态库模块尽量减少引出函数的数目,若确实较多,windows下可以通过def文件中对每个函数加NONAME属性去掉函数名字,只存储序数,从而减小dll文件大小。代码优化对于确定不会抛出异常的函数和方法,在函数/方法定义的末尾加上throw()说明,可以让编译器优化调用此函数/方法的代码。此时要确保此函数/方法不抛异常,就要自己检查函数体内每个调用,可能抛出异常的,要自己在该函数/方法中捕获该异常。如有可能,使用编译器支持的内部函数。不能忽略任何编译信息,包括警告信息。代码优化整数运算:

对于某些整数运算,可以用与、或、非、移位、加减等操作来替代较耗时的乘法、除法、幂指数、对数等运算。如:代码优化上面的代码求得不小于无符号整数x的2的幂。一般的做法是需要做一个循环,不断除2到0,根据循环次数计算幂。而上述代码显然生成的代码更简单,运算更快速。参考书:《高效程序的奥秘》代码优化配置编译器优化选项:

如:确保连接器配置为去除无用的函数和类,并设置为尺寸最小化而不是速度最大化(由于Cache命中的提高,会产生更好的运行效果)(注意在使用这项设置时检查instrinsic功能是否也处于打开状态),浮点优化选项等。

使用异常会对性能造成影响,所以要综合考虑程序鲁棒性和运行效率,针对程序不同地方的不同要求做出合适的选择。代码优化编译选项若启用运行时类型信息(RTTI),编译器会为每一个类产生一些静态信息。RTTI一般来说是缺省启用的,这样我们的代码可以调用dynamic_cast以及检测一个对象的类型。可以考虑完全禁止使用RTTI和dynamic_cast以节省空间(有时候dynamic_cast在某些实现中需要付出很高的代价)。另一方面,当真的需要有基于类型的不同行为的时候,增加一个不同行为的虚函数是更好的面向对象设计(注意static_cast与此不同,它的效率和C语言的类型转换一样)。代码优化注意开发时使用的库以及第三方库(如boost)、共享库:要了解其系统开销,有源代码的可以调试其源代码,若无,可用工具测量其性能是否满足时间要

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论