青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

More Effective C++ (1)

Posted on 2010-01-18 14:16 rikisand 閱讀(356) 評論(0)  編輯 收藏 引用 所屬分類: 工作記錄~~everyday 、C/C++

第四章  效率

······條款16 記住80-20準(zhǔn)則

大約20%的代碼使用了80%的資源,程序的整體性能是由該程序的一小部分代碼所決定的~

可行的辦法是使用程序分析器(profiler)來找到導(dǎo)致性能瓶頸的拿20%的程序~

而且要針對造成瓶頸的資源來使用相應(yīng)的分析器~

 

······條款17  考慮使用延遲計算

延遲計算: 也就是說知道程序要求給出結(jié)果的時候才進(jìn)行運算~ 很好理解,和操作系統(tǒng)中的cow copy on write 一個原理~

四個使用場景:

~1~ 引用計數(shù) :

  class String{…};

String s1 = “hello”;

String s2 = s1 ;      //call string Copy ctor

通常情況下,s2賦值后會有一個hello的拷貝,者通常需要使用new操作符分配內(nèi)存,之后strcpys1

的數(shù)據(jù)給他,但如果下面的操作如下的話:

cout << s1 ;

cout << s1 + s2;

這種情況下如果只增加s1的引用計數(shù),而s2只是共享s1的值就可以了。只有在需要對s2進(jìn)行修改或者s1進(jìn)行修改時,才需要真正拷貝給s2一個副本,引用計數(shù)的實現(xiàn)在29條款

~2~區(qū)分讀寫操作

如: String s = “homer’s all”;

cout<< s[3];

s[3] = ‘x’;

在進(jìn)行讀操作時,使用引用計數(shù)是開銷很小的,然而寫操作必須生成新的拷貝。通過條款30的代理類我們可以把判斷讀寫操作推遲到我們能夠決定哪個是正確操作的時候

~3~延遲讀取

假設(shè)程序使用了包含許多數(shù)據(jù)成員的大對象,這些對象必須在每次程序運行的時候保留下來,因此存進(jìn)了數(shù)據(jù)庫。某些時候從database中取出所有數(shù)據(jù)是沒有必要的,比如他只訪問該對象中的一個數(shù)據(jù)成員。此時,應(yīng)該對對象進(jìn)行處理,只有對象內(nèi)部某一個特定的數(shù)據(jù)成員被訪問的時候才把他取出來。類似于os中的按需換頁~

class LargeObject{

    LargeObject(ObjectID id);

const string& field1() const;

int field2() const;

double field3() const;

const string& field4() const;

private:

ObjectID id;

mutable string* field1value;

mutable int   * fieldValue;

};

LargeObject::LargeObject(ObjectID id):oid(id),fieldValue(0),…{}

const string& LargeObject::field1()const{

   if(fieldValue == 0){

       //read the data for field 1 from database and make field1 point to it

   }

   return  *field1Value;

}

實施lazy fetching 任何成員函數(shù)都需要初始化空指針以指向有效數(shù)據(jù)。但是const成員函數(shù)中,試圖修改數(shù)據(jù)編譯器會報錯。所以聲明字段指針為 mutable ,表示任何函數(shù)都可以修改,即便在const成員函數(shù)中也可以~ 條款28中的智能指針可以讓這一方法更靈活

~3~延遲表達(dá)式求值

數(shù)值計算領(lǐng)域,也在使用延遲計算。例如

matrix<int> m1(1000,1000);

matrix<int> m2(1000,1000);

matrix<int> m3 = m1 + m2;

如果此時計算出m3的話運算量非常之大~

但是如果此時程序為:

m3 = m4*m1;

那么剛才的計算就沒必要了

如果cout<< m3[4];

我們只需要計算m3[4]就可以了,其他的值等到確實需要他們的時候才予以計算~如果運氣夠好的話永遠(yuǎn)不需要計算~

總結(jié),延遲計算只有當(dāng)軟件在某種程度下可以被避免時候才有意義~只有延遲計算得到的好處大于設(shè)計它與實現(xiàn)它花費的精力時才有意義~

·······條款18: 分期攤還預(yù)期的計算開銷

提前計算~ over-eager evaluation 在系統(tǒng)要求你做某些事情之前就做了他~

例如:大量數(shù)據(jù)的集合

template<class NumericalType>

class DataCollection}{

public:

  NumericalType min() const;

  NumericalType max() const;

  NumericalType avg() const;

};

使用提前計算,我們隨時跟蹤目前集合的最大最小平均值,這樣 min max avg被調(diào)用時候,我們可以不用計算立刻返回正確的數(shù)值~~

提前計算的思想便是:如果預(yù)計某個計算會被頻繁調(diào)用,你可以通過設(shè)計你的數(shù)據(jù)結(jié)構(gòu)以更高效的辦法處理請求,這樣可以降低每次請求的平均開銷~

最簡單的做法為 緩存已經(jīng)計算過并且很可能不需要重新計算的那些值~

例如在數(shù)據(jù)庫中存有很多辦公室的電話號碼,程序在每次查詢電話時先查詢本地的緩存如果沒找到再去訪問數(shù)據(jù)庫,并且更新緩存,這樣使用緩存平均訪問時間要大大減小。

預(yù)處理也是一種策略。

例如設(shè)計動態(tài)數(shù)組的時候,當(dāng)索引下標(biāo)大于已有最大范圍時候,需要new出新的空間,如果申請兩倍于索引的大小的話就可以避免頻繁的申請操作~~~

 

········條款 19 : 了解臨時對象的來源

如果一個對象被創(chuàng)建,不是在堆上,沒有名字,那么這個對象就是臨時對象。

通常產(chǎn)生于: 為了使函數(shù)調(diào)用能夠成功而進(jìn)行的隱式轉(zhuǎn)換,或者函數(shù)返回對象是進(jìn)行的隱式轉(zhuǎn)換。由于構(gòu)造和析構(gòu)他們帶來的開銷可以給你的程序帶來顯著的影響,因此有必要了解他們~

~1首先考慮為了函數(shù)調(diào)用能通過產(chǎn)生的臨時對象的情況

傳給某個函數(shù)的對象的類型和這個函數(shù)所綁定的參數(shù)類型不一致的情況下會出現(xiàn)這種情況。

例如:

size_t count(const string& str,char ch);

函數(shù)定義為計算str中ch的數(shù)量

char buffer[100];

cout<<count(buffer,‘c’);

傳入的是一個char數(shù)組,此時編譯器會調(diào)用str的構(gòu)造函數(shù),利用buffer來創(chuàng)建一個臨時對象。

在調(diào)用完countChar語句后這個臨時對象就被自動銷毀了~

僅當(dāng)傳值或者const引用的時候才會發(fā)生這樣的類型轉(zhuǎn)換~當(dāng)傳遞一個非常量引用的時候,不會發(fā)生。

void uppercasify(string& str); //change all chars in str to upper case;

在這個例子中使用char數(shù)組就不會成功~

因為程序作者聲明非常量引用也就是想讓對引用的修改反映在他引用的對象身上,但是如果此時生成了臨時對象,那么這些修改只是作用在臨時對象身上,也就不是作者的本意了。所以c++禁止非常量引用產(chǎn)生臨時對象。

~2 函數(shù)返回對象時候會產(chǎn)生臨時對象

例如: const Number operator + ( const Number& lhs,const Number& rhs);

這個函數(shù)返回一個臨時對象,因為他沒有名字,只是函數(shù)的返回值。

條款20中 ,會介紹讓編譯器對已經(jīng)超出生存周期的臨時對象進(jìn)行優(yōu)化

 

········條款20: 協(xié)助編譯器實現(xiàn)返回值優(yōu)化

返回值優(yōu)化:返回帶有參數(shù)的構(gòu)造函數(shù)。

cosnt Rational operator * (cosnt Rational& lhs,const Rational& rhs){

    return Rational(lhs.numerator()*rhs.numerator(),lhs.denomiator()*rhs.denominator()};

c++允許編譯器針對超出生命周期的臨時對象進(jìn)行優(yōu)化。因此如果調(diào)用Rational c=a*b;

c++允許編譯器消除operator*內(nèi)部的臨時變量以及operator*返回的臨時變量,編譯器可以把return表達(dá)式所定義的返回對象構(gòu)造在分配給c的內(nèi)存上。如果這樣做的話那么調(diào)用operator*所產(chǎn)生的臨時對象所帶來的開銷就是0~ 我們可以把operator 聲明為內(nèi)聯(lián)函數(shù)而去除調(diào)用構(gòu)造函數(shù)帶來的開銷~

#include <iostream>
#include <string>
#include "time.h"
using namespace std;
char buffer[100];
class number{
public:
    const friend  number operator * (const number& rhs,const number lhs);
    number(){}
    number(int b):a(b){}
    number(const number& rhs){
        a = rhs.a;
    }
      int a;
};
const number operator*(const number& rhs,const number lhs){
    number res;
    res.a = rhs.a * lhs.a;
        return res;
    /*return number(rhs.a*lhs.a);*/
}
//CLOCKS_PER_SEC
int main()
{
    clock_t start = clock();
    number A(5);number B(6);
    for(int i=0;i<100000000;i++)
        number C = A*B;

    clock_t end = clock();
    cout<<double(end-start)/CLOCKS_PER_SEC<<endl;
}

通過上面的程序運行 如果沒有返回值優(yōu)化 運行時間 15.9s 優(yōu)化后是 10.1s

還是很顯著的么 快了33% ,如果這種情況出現(xiàn)在程序的熱點處~效果就很好了

 

·········條款21 : 通過函數(shù)重載避免隱式類型轉(zhuǎn)換

例子:

class upint{

public:

upint();

upint(int value);

};

cosnt upint operator+(const upint&lhs,const upint&rhs);

upint up1,up2;

upint up3 = up1+up2;

upi3 = up1 +10;

upi4 = 10+ upi2;

這些語句也可以通過,因為創(chuàng)建了臨時對象,通過帶有int的構(gòu)造函數(shù)產(chǎn)生了臨時的upint對象,如果我們不愿意為這些臨時對象的產(chǎn)生與析構(gòu)付出代價,我們需要做什么:

我們聲明 cosnt upint operator+(cosnt upint&lhs,int rhs);

cosnt upint operator+(int lhs,const upint& rhs);

就可以去除臨時對象產(chǎn)生了~

但是如果我們寫了 const upint operator+(int lhs,int rhs); // 錯了~

c++規(guī)定,每一個被重載的運算符必須至少有一個參數(shù)屬于用戶自定義類型,int并不是自定義類型所以上面的不對的

同樣的如果希望string char* 作為參數(shù)的函數(shù),都有理由進(jìn)行重載而避免隱形類型轉(zhuǎn)換(僅僅在有必要的時候,也就是說他們可以對程序效率起到很大幫助的時候~)

··········條款: 考慮使用 op = 來取代 單獨的 op運算符

class Rational{

public:

   Rational& operator+=(const Rational& rhs);

   Rational& operator-=(const Rational& rhs);

}

const Rational operator+(cosnt Rational& lhs,const Rational & rhs){

    return Rational(lhs)+=rhs;

}

利用+= -=來實現(xiàn)+ -可以保證運算符的賦值形式與單獨使用運算符之間存在正常的關(guān)系。

Rational a,b,c,d,result;

result = a+ b+c+d; // 可能要用到3個臨時對象

result +=a;result+=b;result+=c; //沒有臨時對象

前者書寫維護(hù)都更容易,而且一般來說效率不存在問題,但是特殊情況下后者效率更高更可取

注意:

如果+的實現(xiàn)是這樣的:

const T operator+ (constT& lhs,const T&rhs){

     T result(lhs);

     return result += rhs;

}

這個模版中包含有名字對象result,這個對象有名字意味著返回值優(yōu)化不可用~~~~~~~~·

 

·······條款23 : 考慮使用其他等價的程序庫

主旨:

提供類似功能的程序庫通常在性能問題上采取不同的權(quán)衡措施,比如iostream和stdio,所以通過分析程序找到軟件瓶頸之后,可以考慮是否通過替換程序庫來消除瓶頸~~~~

 

 

······條款24 : 理解虛函數(shù),多重繼承,虛基類以及 RTTI 帶來的開銷

虛函數(shù)表:vtabs 指向虛函數(shù)表的指針 vptrs

程序中每個聲明了或者繼承了的虛函數(shù)的類都具有自己的虛函數(shù)表。表中的各個項就是指向虛函數(shù)具體實現(xiàn)的指針。

class c1{

   c1();

   virtual ~c1();

   virtual void f1();

   virtual int f2(char c)const;

   virtual void f3(const string& s);

};

c1 的虛函數(shù)表包括: c1::~c1 c1::f1 c1::f2 c1::f3

class c2:public c1{

   c2();

   virtual ~c2();

   virtual void f1();

   virtual void f5(char *str);

};

它的虛函數(shù)表入口指向的是那些由c1聲明但是c2沒有重定義的虛函數(shù)指針:

c2::~c2  c2::f1 c1::f2 c1::f3 c2::f5

所以開銷上: 必須為包含虛函數(shù)的類騰出額外的空間來存放虛函數(shù)表。一個類的虛函數(shù)表的大小取決于它的虛函數(shù)的個數(shù),雖然每一個類只要有一個虛函數(shù)表,但是如果有很多類或者每個類具有很多個虛函數(shù),虛函數(shù)表也會占據(jù)很大的空間,這也是mfc沒有采用虛函數(shù)實現(xiàn)消息機制的一個原因。

由于每一個類只需要一個vtbl的拷貝,把它放在哪里是一個問題:

一種:為每一個需要vtbl的目標(biāo)文件生成拷貝,然后連接時取出重復(fù)拷貝

或者:更常見的是采用試探性算法決定哪一個目標(biāo)文件應(yīng)該包含類的vtbl。試探:一個類的vtbl通常產(chǎn)生在包含該類第一個非內(nèi)聯(lián),非純虛函數(shù)定義的目標(biāo)文件里。所以上面c1類的vtbl將放在c1::~c1 定義的目標(biāo)文件里。如果所有虛函數(shù)都聲明為內(nèi)聯(lián),試探性算法就會失敗,在每一個目標(biāo)文件就會有vtbl。所以一般忽略虛函數(shù)的inline指令。

如果一個類具有虛函數(shù),那么這個類的每一個對象都會具有指向這個虛函數(shù)表的指針,這是一個隱藏數(shù)據(jù)成員vptr~被編譯器加在某一個位置。

此處第二個開銷:你必須在每一個對象中存放一個額外的指針~

如果對象很小這個開銷就十分顯著~~因為比例大~

此時 void makeCall(c1* pc1){

   pc1->f1();

}

翻譯為 (*pc1->vptr[i])(pc1);

根據(jù)vptr找到vtbl 這很簡單,

在vtbl找到調(diào)用函數(shù)對應(yīng)的函數(shù)指針,這個步驟也很簡單,因為編譯器為虛函數(shù)表里的每一個函數(shù)設(shè)置了唯一的索引

然后調(diào)用指針?biāo)赶虻暮瘮?shù)~

這樣看來,調(diào)用虛函數(shù)與普通函數(shù)調(diào)用的效率相差無幾,只多出幾個指令。

虛函數(shù)真正的開銷與內(nèi)聯(lián)函數(shù)有關(guān)~:在實際應(yīng)用中,虛函數(shù)不應(yīng)該被內(nèi)聯(lián),因為內(nèi)聯(lián)意味著在編譯時刻用被調(diào)用函數(shù)的函數(shù)體來代替被調(diào)用函數(shù)。但是虛函數(shù)意味著運行時刻決定調(diào)用哪個一函數(shù),so~~~虛函數(shù)付出的第三個代價?。簙不能內(nèi)聯(lián)(通過對象調(diào)用虛函數(shù)的時候,這些虛函數(shù)可以內(nèi)聯(lián),但是大多數(shù)虛函數(shù)通過指針或者以用來調(diào)用的)。

~多重繼承的情況

多重繼承一般要求虛基類。沒有虛基類,如果一個派生類具有多個通向基類的繼承路徑,基類的數(shù)據(jù)成員會被復(fù)制到每一個繼承類對象里,繼承類與基類間的每一條路徑都有一個拷貝。

有了虛基類,通常使用指向虛基類的指針作為避免重復(fù)的手段,這樣需要在對象內(nèi)部嵌入一個或者多個指針~也帶來了一定的開銷~

例如菱形繼承 :

class A{};

class B:virtual public A{};

class C:virtual public A{};

class D:public B,public C{};

這里A是一個虛基類,因為B和C虛擬繼承了他。

對象 D 的布局:

B data

vptr

pointer to virtual base class

C data

vptr

pointer to virtual base class

D data members

A data members

vptr

上面四個類,只有三個vptr,因為B和D可以共享一個vptr  (為啥?)

現(xiàn)在我們已經(jīng)看到虛函數(shù)如何使對象變得更大,以及為何不能把它內(nèi)聯(lián)了~

下面我們看看RTTI的開銷 runtime type identifycation 所需要的開銷

通過rtti我們可以知道對象和類的有關(guān)信息,所以肯定在某個地方存儲了這些供我們查詢的信息,這些信息被存儲在type_info 類型的對象里,你可以通過typeid運算符訪問一個類的type_info對象。

每個類僅僅需要一個RTTI的拷貝,規(guī)范上只保證提供哪些至少有一個虛函數(shù)的對象的準(zhǔn)確的動態(tài)類型信息~

why?和虛函數(shù)有啥關(guān)系~ 因為rtti設(shè)計在vtbl里

vtbl的下標(biāo)0包含指向type_info對象的指針。所以使用這種實現(xiàn)方法,消費的空間是vtbl中占用一個額外的單元再加上存儲type_info對象所需要的空間。

 

------------------------罪惡的結(jié)束線 OVER~------------------------------------------


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            在线亚洲精品| 久久视频免费观看| 国产一区二区三区久久久| 欧美色视频日本高清在线观看| 欧美高清在线视频观看不卡| 欧美精品日韩| 欧美午夜精品久久久久久人妖| 欧美午夜精彩| 国内偷自视频区视频综合| 狠狠色狠狠色综合人人| 亚洲国产欧美不卡在线观看| 一区二区三区精品在线| 午夜宅男久久久| 久久久久国产一区二区| 亚洲福利在线看| 亚洲激情不卡| 性久久久久久久久久久久| 久久久免费av| 欧美三级在线播放| 国产综合色在线视频区| 日韩五码在线| 久久精品视频免费| 欧美午夜性色大片在线观看| 一区二区三区精品久久久| 亚洲一区区二区| 老司机精品视频一区二区三区| 欧美精品首页| 激情综合网址| 亚洲免费一级电影| 亚洲国产精品一区| 欧美在线免费看| 国产精品理论片在线观看| 亚洲精品久久久久久久久久久| 欧美怡红院视频| 亚洲精品国产精品国自产观看| 欧美在线亚洲在线| 国产精品v欧美精品v日韩| 亚洲国产精品一区二区三区| 午夜在线观看免费一区| 亚洲欧洲日夜超级视频| 久久亚洲综合色| 国产一区二区三区日韩| 欧美一区二区高清| 在线综合亚洲| 欧美美女日韩| 亚洲精品免费在线| 欧美11—12娇小xxxx| 欧美一区激情| 国产精品主播| 欧美一区二区国产| 亚洲一区二区三区精品视频| 欧美午夜精品| 亚洲欧美国产一区二区三区| 99热免费精品在线观看| 欧美日韩精品欧美日韩精品| 一区二区三欧美| 亚洲老板91色精品久久| 欧美顶级少妇做爰| 亚洲美女av网站| 亚洲第一免费播放区| 乱人伦精品视频在线观看| 在线成人免费观看| 男人插女人欧美| 欧美成人首页| 日韩一级免费| 亚洲最新在线| 国产精品资源在线观看| 久久精品99国产精品| 久久久精品动漫| 亚洲高清视频在线| 最新日韩中文字幕| 欧美性大战xxxxx久久久| 亚洲综合视频一区| 欧美一级欧美一级在线播放| 尤物精品在线| 亚洲激情第一区| 国产精品porn| 久久免费视频观看| 欧美va亚洲va国产综合| 亚洲图色在线| 午夜欧美理论片| 亚洲第一精品久久忘忧草社区| 欧美在线精品免播放器视频| 久久久久久香蕉网| 欧美在线影院| 亚洲精品裸体| 妖精视频成人观看www| 国产精品久久一卡二卡| 久久一区二区三区超碰国产精品| 老司机67194精品线观看| 亚洲视频在线观看视频| 欧美伊人久久| 99精品热视频只有精品10| 亚洲免费婷婷| 最新日韩在线视频| 亚洲综合社区| 亚洲免费观看高清在线观看 | 亚洲韩国日本中文字幕| 亚洲黄一区二区| 国产亚洲精品高潮| 亚洲欧洲日本专区| 激情综合网激情| 亚洲一级黄色| 一本久久综合亚洲鲁鲁| 性高湖久久久久久久久| 中国成人黄色视屏| 狼人社综合社区| 欧美在线91| 欧美日韩少妇| 欧美福利视频| 国产一区二区高清不卡| 一本高清dvd不卡在线观看| 有码中文亚洲精品| 午夜视频在线观看一区| 亚洲淫片在线视频| 欧美精品啪啪| 欧美激情网站在线观看| 国内精品久久久久久久果冻传媒 | 中日韩美女免费视频网站在线观看| 久久精品噜噜噜成人av农村| 亚洲天堂av在线免费观看| 女女同性精品视频| 久久婷婷久久一区二区三区| 国产精品在线看| 中文日韩欧美| 亚洲一区二区三区欧美| 欧美日韩国产综合视频在线| 欧美不卡三区| 1769国产精品| 玖玖精品视频| 欧美成人亚洲成人日韩成人| 怡红院av一区二区三区| 久久噜噜噜精品国产亚洲综合| 欧美在线视频观看免费网站| 国产精品高清网站| 亚洲一区日本| 欧美在线视频一区| 国产一区二区三区黄| 午夜亚洲一区| 久久久久天天天天| 在线精品一区| 欧美va亚洲va香蕉在线| 亚洲黄色片网站| 亚洲精品小视频在线观看| 欧美电影打屁股sp| 国产精品高潮粉嫩av| 一区二区三区黄色| 欧美在线亚洲综合一区| 国产亚洲欧美中文| 久久视频在线免费观看| 亚洲国产中文字幕在线观看| 一区二区三区四区五区精品视频 | 国产精品揄拍500视频| 亚洲一区二区黄色| 久久久久天天天天| 亚洲黄色大片| 欧美日韩亚洲一区在线观看| 99精品国产高清一区二区| 亚洲午夜一区| 国产精品视频一| 久久久视频精品| 91久久综合| 欧美一级大片在线观看| **性色生活片久久毛片| 欧美欧美午夜aⅴ在线观看| 亚洲欧美精品在线| 欧美成人性生活| 亚洲欧美综合另类中字| 国内激情久久| 欧美视频不卡中文| 久久久久网址| 一区二区三区欧美亚洲| 米奇777在线欧美播放| 在线视频精品一区| 国产综合色一区二区三区| 欧美极品色图| 久久久99国产精品免费| 99精品欧美一区二区三区综合在线| 欧美一区二区在线免费观看| 亚洲激情在线| 国语精品一区| 国产精品免费网站在线观看| 老司机久久99久久精品播放免费 | 欧美视频日韩视频| 久久女同精品一区二区| 亚洲视频一区在线| 亚洲福利久久| 久久久精品国产99久久精品芒果| 中文欧美日韩| 亚洲精品欧美日韩专区| 黑人一区二区| 国产一区二区精品丝袜| 国产酒店精品激情| 国产精品扒开腿做爽爽爽视频 | 国产视频一区三区| 欧美视频一区二区三区| 欧美国产国产综合| 久热精品在线视频| 久久精品亚洲一区二区三区浴池 | 欧美日韩你懂的|