C++代码优化经验总结.docx
- 文档编号:11987655
- 上传时间:2023-04-16
- 格式:DOCX
- 页数:19
- 大小:23.48KB
C++代码优化经验总结.docx
《C++代码优化经验总结.docx》由会员分享,可在线阅读,更多相关《C++代码优化经验总结.docx(19页珍藏版)》请在冰豆网上搜索。
C++代码优化经验总结
C++代码优化经验总结
优化是一个非常大的主题,本文并不是去深入探讨性能分析理论,算法的效率,况且我也没有这个能力。
我只是想把一些可以简单的应用到你的C++代码中的优化技术总结在这里,这样,当你遇到几种不同的编程策略的时候,就可以对每种策略的性能进行一个大概的估计。
这也是本文的目的之所在.
目录:
一.优化之前
二.声明的放置
三.内联函数
四.优化你的内存使用
五.速度优化
六.最后的求助
一.优化之前
在进行优化之前,我们首先应该做的是发现我们代码的瓶颈(bottleneck)在哪里。
然而当你做这件事情的时候切忌从一个debug-version进行推断,因为debug-version中包
含了许多额外的代码。
一个debug-version可执行体要比release-version大出40%。
那些额
外的代码都是用来支持调试的,比如说符号的查找。
大多数实现都为debug-version和rele
ase-version提供了不同的operatornew以及库函数。
而且,一个release-version的执行
体可能已经通过多种途径进行了优化,包括不必要的临时对象的消除,循环展开,把对象
移入寄存器,内联等等。
另外,我们要把调试和优化区分开来,它们是在完成不同的任务。
debug-version是
用来追捕bugs以及检查程序是否有逻辑上的问题。
release-version则是用来做一些性能上
的调整以及进行优化。
下面就让我们来看看有哪些代码优化技术吧!
二.声明的放置
程序中变量和对象的声明放在什么位置将会对性能产生显著影响。
同样,对postfix和
prefix运算符的选择也会影响性能。
这一部分我们集中讨论四个问题:
初始化v.s赋值,
在程序确实要使用的地方放置声明,构造函数的初始化列表,prefixv.spostfix运算符
。
(1)请使用初始化而不是赋值
在C语言中只允许在一个函数体的开头进行变量的声明,然而在C++中声明可以出现在
程序的任何位置。
这样做的目的是希望把对象的声明拖延到确实要使用它的时候再进行。
这样做可以有两个好处:
1.确保了对象在它被使用前不会被程序的其他部分恶意修改。
如
果对象在开头就被声明然而却在20行以后才被使用的话,就不能做这样的保证。
2.使我们
有机会通过用初始化取代赋值来达到性能的提升,从前声明只能放在开头,然而往往开始
的时候我们还没有获得我们想要的值,因此初始化所带来的好处就无法被应用。
但是现在
我们可以在我们获得了想要的值的时候直接进行初始化,从而省去了一步。
注意,或许对
于基本类型来说,初始化和赋值之间可能不会有什么差异,但是对于用户定义的类型来说
,二者就会带来显著的不同,因为赋值会多进行一次函数调用----operator=。
因此当我
们在赋值和初始化之间进行选择的话,初始化应该是我们的首选。
(2)把声明放在合适的位置上
在一些场合,通过移动声明到合适的位置所带来的性能提升应该引起我们足够的重视
。
例如:
boolis_C_Needed();
voiduse()
{
Cc1;
if(is_C_Needed()==false)
{
return;//c1wasnotneeded
}
//usec1here
return;
}
上面这段代码中对象c1即使在有可能不使用它的情况下也会被创建,这样我们就会为它付
出不必要的花费,有可能你会说一个对象c1能浪费多少时间,但是如果是这种情况呢:
C
c1[1000];我想就不是说浪费就浪费了。
但是我们可以通过移动声明c1的位置来改变这种情
况:
voiduse()
{
if(is_C_Needed()==false)
{
return;//c1wasnotneeded
}
Cc1;//movedfromtheblock'sbeginning
//usec1here
return;
}
怎么样,程序的性能是不是已经得到很大的改善了呢?
因此请仔细分析你的代码,把声明
放在合适的位置上,它所带来的好处是你难以想象的。
(3)初始化列表
我们都知道,初始化列表一般是用来初始化const或者reference数据成员。
但是由于
他自身的性质,我们可以通过使用初始化列表来实现性能的提升。
我们先来看一段程序:
classPerson
{
private:
Cc_1;
Cc_2;
public:
Person(constC&c1,constC&c2):
c_1(c1),c_2(c2){}
};
当然构造函数我们也可以这样写:
Person:
:
Person(constC&c1,constC&c2)
{
c_1=c1;
c_2=c2;
}
那么究竟二者会带来什么样的性能差异呢,要想搞清楚这个问题,我们首先要搞清楚二者
是如何执行的,先来看初始化列表:
数据成员的声明操作都是在构造函数执行之前就完成
了,在构造函数中往往完成的只是赋值操作,然而初始化列表直接是在数据成员声明的时
候就进行了初始化,因此它只执行了一次copyconstructor。
再来看在构造函数中赋值的
情况:
首先,在构造函数执行前会通过defaultconstructor创建数据成员,然后在构造函
数中通过operator=进行赋值。
因此它就比初始化列表多进行了一次函数调用。
性能差异
就出来了。
但是请注意,如果你的数据成员都是基本类型的话,那么为了程序的可读性就
不要使用初始化列表了,因为编译器对两者产生的汇编代码是相同的。
(4)postfixVSprefix运算符
prefix运算符++和—比它的postfix版本效率更高,因为当postfix运算符被使用的时
候,会需要一个临时对象来保存改变以前的值。
对于基本类型,编译器会消除这一份额外
的拷贝,但是对于用户定义类型,这似乎是不可能的。
因此请你尽可能使用prefix运算符
。
三.内联函数
内联函数既能够去除函数调用所带来的效率负担又能够保留一般函数的优点。
然而,
内联函数并不是万能药,在一些情况下,它甚至能够降低程序的性能。
因此在使用的时候
应该慎重。
1.我们先来看看内联函数给我们带来的好处:
从一个用户的角度来看,内联函数看起来和
普通函数一样,它可以有参数和返回值,也可以有自己的作用域,然而它却不会引入一般
函数调用所带来的负担。
另外,它可以比宏更安全更容易调试。
当然有一点应该意识到,inlinespecifier仅仅是对编译器的建议,编译器有权利忽
略这个建议。
那么编译器是如何决定函数内联与否呢?
一般情况下关键性因素包括函数体
的大小,是否有局部对象被声明,函数的复杂性等等。
2.那么如果一个函数被声明为inline但是却没有被内联将会发生什么呢?
理论上,当编译
器拒绝内联一个函数的时候,那个函数会像普通函数一样被对待,但是还会出现一些其他
的问题。
例如下面这段代码:
//Time.h
#include
#include
usingnamespacestd;
classTime
{
public:
inlinevoidShow(){for(inti=0;i<10;i++)cout<
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 代码 优化 经验总结