国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

C++代碼優化方法總結(1)

2019-11-17 05:42:44
字體:
來源:轉載
供稿:網友
優化是一個非常大的主題,本文并不是去深入探討性能分析理論,算法的效率,況

且我也沒有這個能力。我只是想把一些可以簡單的應用到你的C++代碼中的優化技

術總結在這里,這樣,當你碰到幾種不同的編程策略的時候,就可以對每種策略的

性能進行一個大概的估計。這也是本文的目的之所在。

一. 優化之前

在進行優化之前,我們首先應該做的是發現我們代碼的瓶頸(bottleneck)在哪里

。然而當你做這件事情的時候切忌從一個debug-version進行推斷,因為

debug-version中包含了許多額外的代碼。一個debug-version可執行體要比

release-version大出40%。那些額外的代碼都是用來支持調試的,比如說符號的查

找。大多數實現都為debug-version和release-version提供了不同的Operator

new以及庫函數。而且,一個release-version的執行體可能已經通過多種途徑進行

了優化,包括不必要的臨時對象的消除,循環展開,把對象移入寄存器,內聯等等



另外,我們要把調試和優化區分開來,它們是在完成不同的任務。 debug-version

是用來追捕bugs以及檢查程序是否有邏輯上的問題。release-version則是用來做

一些性能上的調整以及進行優化。

下面就讓我們來看看有哪些代碼優化技術吧:

二. 聲明的放置

程序中變量和對象的聲明放在什么位置將會對性能產生顯著影響。同樣,對

postfix和PRefix運算符的選擇也會影響性能。這一部分我們集中討論四個問題:

初始化v.s 賦值,在程序確實要使用的地方放置聲明,構造函數的初始化列表,

prefix v.s postfix運算符。

(1) 請使用初始化而不是賦值

在C語言中只答應在一個函數體的開頭進行變量的聲明,然而在C++中聲明可以出現

在程序的任何位置。這樣做的目的是希望把對象的聲明拖延到確實要使用它的時候

再進行。這樣做可以有兩個好處:1. 確保了對象在它被使用前不會被程序的其他

部分惡意修改。假如對象在開頭就被聲明然而卻在20行以后才被使用的話,就不能

做這樣的保證。2. 使我們有機會通過用初始化取代賦值來達到性能的提升,從前

聲明只能放在開頭,然而往往開始的時候我們還沒有獲得我們想要的值,因此初始

化所帶來的好處就無法被應用。但是現在我們可以在我們獲得了想要的值的時候直

接進行初始化,從而省去了一步。注重,或許對于基本類型來說,初始化和賦值之

間可能不會有什么差異,但是對于用戶定義的類型來說,二者就會帶來顯著的不同

,因為賦值會多進行一次函數調用----operator =。因此當我們在賦值和初始化之

間進行選擇的話,初始化應該是我們的首選。

(2) 把聲明放在合適的位置上

在一些場合,通過移動聲明到合適的位置所帶來的性能提升應該引起我們足夠的重

視。例如:

bool is_C_Needed();

void use()

{

C c1;

if (is_C_Needed() == false)

{

return; //c1 was not needed

}

//use c1 here

return;

}

上面這段代碼中對象c1即使在有可能不使用它的情況下也會被創建,這樣我們就會

為它付出不必要的花費,有可能你會說一個對象c1能浪費多少時間,但是假如是這

種情況呢:C c1[1000];我想就不是說浪費就浪費了。但是我們可以通過移動聲明

c1的位置來改變這種情況:

void use()

{

if (is_C_Needed() == false)

{

return; //c1 was not needed

}

C c1; //moved from the block's beginning

//use c1 here

return;

}

怎么樣,程序的性能是不是已經得到很大的改善了呢?因此請仔細分析你的代碼,

把聲明放在合適的位置上,它所帶來的好處是你難以想象的。

(3) 初始化列表

我們都知道,初始化列表一般是用來初始化const或者reference數據成員。但

是由于他自身的性質,我們可以通過使用初始化列表來實現性能的提升。我們先來

看一段程序:

class Person

{

private:

C c_1;

C c_2;

public:

Person(const C& c1, const C& c2 ): c_1(c1), c_2(c2) {}

};

當然構造函數我們也可以這樣寫:

Person::Person(const C& c1, const C& c2)

{

c_1 = c1;

c_2 = c2;

}

那么究竟二者會帶來什么樣的性能差異呢,要想搞清楚這個問題,我們首先要搞清

楚二者是如何執行的,先來看初始化列表:數據成員的聲明操作都是在構造函數執

行之前就完成了,在構造函數中往往完成的只是賦值操作,然而初始化列表直接是

在數據成員聲明的時候就進行了初始化,因此它只執行了一次copy constrUCtor。

再來看在構造函數中賦值的情況:首先,在構造函數執行前會通過default

constructor創建數據成員,然后在構造函數中通過operator =進行賦值。因此它

就比初始化列表多進行了一次函數調用。性能差異就出來了。但是請注重,假如你

的數據成員都是基本類型的話,那么為了程序的可讀性就不要使用初始化列表了,

因為編譯器對兩者產生的匯編代碼是相同的。

(4) postfix VS prefix 運算符

prefix運算符++和—比它的postfix版本效率更高,因為當postfix運算符被使用的

時候,會需要一個臨時對象來保存改變以前的值。對于基本類型,編譯器會消除這

一份額外的拷貝,但是對于用戶定義類型,這似乎是不可能的。因此請你盡可能使

用prefix運算符。
 三. 內聯函數

內聯函數既能夠去除函數調用所帶來的效率負擔又能夠保留一般函數的優點。然而

,內聯函數并不是萬能藥,在一些情況下,它甚至能夠降低程序的性能。因此在使

用的時候應該慎重。

1.我們先來看看內聯函數給我們帶來的好處:從一個用戶的角度來看,內聯

函數看起來和普通函數一樣,它可以有參數和返回值,也可以有自己的作用域,然

而它卻不會引入一般函數調用所帶來的負擔。另外,它可以比宏更安全更輕易調試



當然有一點應該意識到,inline specifier僅僅是對編譯器的建議,編譯器有權

利忽略這個建議。那么編譯器是如何決定函數內聯與否呢?一般情況下要害性因素

包括函數體的大小,是否有局部對象被聲明,函數的復雜性等等。

2.那么假如一個函數被聲明為inline但是卻沒有被內聯將會發生什么呢?理

論上,當編譯器拒絕內聯一個函數的時候,那個函數會像普通函數一樣被對待,但

是還會出現一些其他的問題。例如下面這段代碼:

// filename Time.h

#include<ctime>

#include<iostream>

using namespace std;

class Time

{

public:

inline void Show() { for (int i = 0; i<10; i++)

cout<<time(0)<<endl;}

};


因為成員函數Time::Show()包括一個局部變量和一個for循環,所以編譯器一

般拒絕inline,并且把它當作一個普通的成員函數。但是這個包含類聲明的頭文件

會被單獨的#include進各個獨立的編譯單元中:

// filename f1.cpp

#include "Time.hj"

void f1()

{

Time t1;

t1.Show();

}

// filename f2.cpp

#include "Time.h"

void f2()

{

Time t2;

t2.Show();

}

結果編譯器為這個程序生成了兩個相同成員函數的拷貝:

void f1();

void f2();

int main()

{

f1();

f2();

return 0;

}

當程序被鏈接的時候,linker將會面對兩個相同的Time::Show()拷貝,于是函數重

定義的連接錯誤發生。但是老一些的C++實現對付這種情況的辦法是通過把一個

un-inlined函數當作static來處理。因此每一份函數拷貝僅僅在自己的編譯單元中

可見,這樣鏈接錯誤就解決了,但是在程序中卻會留下多份函數拷貝。在這種情況

下,程序的性能不但沒有提升,反而增加了編譯和鏈接時間以及最終可執行體的大

小。

但是幸運的是,新的C++標準中關于un-inlined函數的說法已經改變。一個符合標

準C++實現應該只生成一份函數拷貝。然而,要想所有的編譯器都支持這一點可能

還需要很長時間。

另外關于內聯函數還有兩個更令人頭疼的問題。第一個問題是該如何進行維護。一

個函數開始的時候可能以內聯的形式出現,但是隨著系統的擴展,函數體可能要求

添加額外的功能,結果內聯函數就變得不太可能,因此需要把inline specifier去

除以及把函數體放到一個單獨的源文件中。另一個問題是當內聯函數被應用在代碼

庫的時候產生。當內聯函數改變的時候,用戶必須重新編譯他們的代碼以反映這種

改變。然而對于一個非內聯函數,用戶僅僅需要重新鏈接就可以了。

這里想要說的是,內聯函數并不是一個增強性能的靈丹妙藥。只有當函數非常短小

的時候它才能得到我們想要的效果,但是假如函數并不是很短而且在很多地方都被

調用的話,那么將會使得可執行體的體積增大。最令人煩惱的還是當編譯器拒絕內

聯的時候。在老的實現中,結果很不盡人意,雖然在新的實現中有很大的改善,但

是仍然還是不那么完善的。一些編譯器能夠

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 东兰县| 安国市| 曲水县| 郧西县| 琼中| 西青区| 定远县| 综艺| 勃利县| 刚察县| 安岳县| 长子县| 临朐县| 科技| 铁岭市| 襄城县| 乡城县| 安塞县| 白山市| 临潭县| 格尔木市| 麻城市| 大厂| 阳春市| 梧州市| 喜德县| 安徽省| 基隆市| 团风县| 禄劝| 类乌齐县| 遂川县| 晋州市| 象山县| 广灵县| 瓦房店市| 迁安市| 巴南区| 乌审旗| 阿城市| 石屏县|