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

天秤座的唐風

總會有一個人需要你的分享~!- 唐風 -

  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
  13 隨筆 :: 0 文章 :: 69 評論 :: 0 Trackbacks

與臨時對象的斗爭(下)

作者:唐風

原載:www.cnblos.com/liyiwen

上篇里,我們看到了 (N)RVO 和右值引用,下面我們來看看表達式模板。

Expression Template(表達式模板,ET)

如果有“系統(tǒng)地”學習過 C++ 的模板編程,那么你應該已經(jīng)知道 Expression Template 這個“東西”。在模板圣經(jīng)《C++ templates》的第 18 章專門用了一整章來講這個技巧,(是的,我認為它是一種技巧)。足以見得它比較復雜,也很重要。

說起 Expression Template 產(chǎn)生,“臨時對象”也是“功臣”之一啊。還是來用例子來說明(你能很容易找到這樣類似的例子,呵,我就是參照著別人寫的):

class MyVec
{
public:
    MyVec(){
        p = new int[SIZE];
    }
    MyVec(MyVec const& a_left) {
        p = new int[SIZE];
        memcpy(p, a_left.p, SIZE * sizeof(int));
    }
    ~MyVec(){delete [] p;}
    MyVec& operator=(MyVec const& a_left) {
        if (this != &a_left) {
            delete [] p;
            p = new int[SIZE];
            memcpy(p, a_left.p, SIZE * sizeof(int));
        }
        return *this;
    }
    int& operator [](size_t a_idx) { 
        return p[a_idx];
    }
    int operator [](size_t a_idx)const { 
        return p[a_idx];
    }
    MyVec const operator + (MyVec const& a_left) const {
        MyVec temp(*this);
        temp += a_left;
        return temp;
    }
    MyVec& operator += (MyVec const& a_left) { 
        for (size_t i = 0; i < SIZE; ++i) {
            p[i] += a_left.p[i];
        }
        return *this;
    }
private:
    static int const SIZE = 100;
    int* p;
};

int main(int argc, char* argv[])
{
    MyVec a, b, c;
    MyVec d = a + b + c;
    return 0;
}

看,我們寫下這么小段代碼:

MyVec d = a + b + c;

這是很常用的數(shù)學運算吧,而且代碼很直觀。但這個表達式有一個問題,就是產(chǎn)生了“不必要”的臨時對象。因為 a + b 的結果會成為一個存在一個臨時對象上 temp 上,然后這個 temp 再加上 c ,最后把結果傳給 d 進行初始化。如果這些向量很長,或是表達式再加幾節(jié),可以想像這些 temp 會多讓人不爽。

而且,如果我們寫成這樣:

MyVec d = a;
d += b;
d += c;

就可以避免產(chǎn)生多余的臨時對象。但這樣寫,如果是不了解“行情”的人看了MyVec d = a + b + c;之后再看這段,是不是會覺得寫這代碼的人欠K?

好吧,你會問,上面不是說右值引用可以解決這樣問題?是的,但在沒有右值引用的“黑暗日子”里,我們就不用過活了?當然要,小學開始數(shù)學老師就教我們要一題多解吧,換個思路也有辦法,這個辦法就是ET。

怎么做的呢?a + b + c 會產(chǎn)生臨時變量是因為 C++ 是即時求值的,在看到 a + b,就先算出一個 temp 的Vector對象,然后再向下算。如果能進行延遲求值,看完整個表達式再來計算,那么就可以避免這個temp的產(chǎn)生。

怎么做?

原來的做法中,operator + 直接進行了計算,既然我們不想它“過早”的計算,那么我們就在重新重載一個operator + 運算符,在這個運算中不進行真正的運算,只是生成一個對象,在這個對象中把加法運算符兩邊的操作數(shù)保留下來~然后讓它參與到下一步的計算中去。(好吧,這個對象也是臨時的,但它的代價非常非常小,我們先不理會它)。于是我們寫下面的代碼:

class MyVec;

template <typename L>
class ExpPlus {
    L const & lvec;
    MyVec const & rvec;
public:
    ExpPlus(L const& a_l, MyVec const& a_r):
      lvec(a_l), rvec(a_r)
      { }
      int operator [] (size_t a_idx) const;
};

// Point 1
template <typename L>
ExpPlus<L> operator + (L const& a_l, MyVec const & a_r) {
    return ExpPlus<L>(a_l, a_r);
}

class MyVec
{
public:
    MyVec(){
        p = new int[SIZE];
    }

    MyVec(MyVec const& a_r) {
        p = new int[SIZE];
        memcpy(p, a_r.p, SIZE * sizeof(int));
    }

    template <typename Exp>
    MyVec(Exp const& a_r) {
        p = new int[SIZE];
        for (size_t i = 0; i < SIZE; ++i) {
            p[i] += a_r[i];
        }
    }

    ~MyVec(){delete [] p;}

    MyVec& operator = (MyVec const& a_r) {
        if (this != &a_r) {
            delete [] p;
            p = new int[SIZE];
            memcpy(p, a_r.p, SIZE * sizeof(int));
        }
        return *this;
    }

    template <typename Exp>
    MyVec& operator = (Exp const & a_r) {
        delete [] p;
        p = new int[SIZE];
        for (size_t i = 0; i < SIZE; ++i) {
            p[i] += a_r[i];
        }
        return *this;
    }

    int& operator [](size_t a_idx) { 
        return p[a_idx];
    }

    int operator [](size_t a_idx)const { 
        return p[a_idx];
    }
private:
    static int const SIZE = 100;
    int* p;
};

template <typename L>
int ExpPlus<L>::operator [] (size_t a_idx) const {
    return lvec[a_idx] + rvec[a_idx];
}

int main(int argc, char* argv[])
{
    MyVec a, b, c;
    MyVec d = a + b + c;
    return 0;
}

比起之前的代碼來說,這段代碼有幾個重要的修改:首先,我們增加了一個模板類 ExpPlus,用它來代表加法計算的“表達式”,但在進行加法時,它本身并不進行真正的計算。對這個類,定義了下標運算符,這個運算符中才進行了真正的加法計算。然后,對于原來的 MyVec,我們重載它的賦值運算符,讓它在賦值的時候通過ExpPlus的下標運算符來獲得計算結果(也就是,在賦值操作時才真正的進行了計算!)。

上面這段話,對于不了解ET的人來說,也許一時間還不容易明白,我們一步一步來:

在 d = a + b + c 這個式子中,首先遇到 a + b,這時,模板函數(shù) operator + 會被調(diào)用(代碼中注釋了“Point 1 ”),這時只是生成一個臨時的ExpPlus<MyVec>對象(我們叫它 t1 吧),不做計算,只是保留計算的左右操作數(shù)(也就是a和b),接著,t1 + c ,再次調(diào)用同樣的 operator + ,而且也只是生成一個對象(我們叫它 t2 吧),這個對象的類型是 ExpPlus<ExpPlus<MyVec>>,同樣,t2 在這里只是保留了兩邊的操作數(shù)(也就是 t1 和 c)。直到整個表達式“做完”,沒有任何東西進行了計算,所做的事情實際上只是用 ExpPlus 這個模板類把計算式的信息記錄下來了(當然,這些信息就是參與計算的操作數(shù))。

最后,當進行 d = t2 的時候,MyVec 的賦值運算符被調(diào)用(用 t2 作參數(shù))。注意,這個調(diào)用中的語句 p[i] = t2[i],其中 t2[i] 通過重載的下標運算符,展開成 t1[i] + c[i],同理 t1[i] 又再次展開,成為 a[i]+b[i],最終,p[i] = t2[i] 就變成了:p[i] = a[i] + b[i] + c[i])(當然,里面參雜了內(nèi)聯(lián)的效果,這些函數(shù)都是非常容易被內(nèi)聯(lián)的)。就像變“魔術”一樣,我們通過ExpPlus完成了“延遲計算”,并避免了大型的 MyVec 臨時對象的產(chǎn)生。

這基本上就是 ET 的“原理”了吧。我們來“專門化”一下 ET 的好處:

  • To create a domain-specific embedded language (DSEL) in C++
  • To support lazy evaluation of C++ expressions (e.g., mathematical expressions), which can be executed much later in the program from the point of their definition.
  • To pass an expression — not the result of the expression — as a parameter to a function.

這樣,用 ET 就能兼顧到“直觀”和“效率”了。

ET 中 C++ 中的類庫里已經(jīng)有非常多的應用了(包括 boost 中的多個子庫,以及 Blitz++ 等高性能數(shù)學庫)

總結

(N)RVO 是編譯器為我們做的優(yōu)化手段,在能進行優(yōu)化的情況下,NRVO 的表現(xiàn)是非常好的,因為它才真正的避免了臨時對象的產(chǎn)生(rvalue reference 和 expression template 中都可能還存在一些小型臨時對象),但 (N)RVO 有很多的限制條件。右值引用(rvalue reference )和 move 語意彌補了 (N)RVO 的不足之處,使得臨時對象的開銷最小化成為可能,但這也是有局限的,比如,嗯,如果一個類本身不動態(tài)地擁有資源……,那 move 就沒有意義了。Expression Template 保持了表達式直觀和效率兩者,很強大,但很顯然它太復雜,主要是作為類庫的設計者的武器。另外,它也可能使得使用者要理解一些“新”東西,比如,如果我想存儲表達式的中間值,那么 <ExpPlus<ExpPlus<...<MyVec>...> 一定會讓我很頭大(不過有了 C++0x 的 auto 就好多了,呵呵)。

 

全文完!

 

 

posted on 2009-12-03 23:45 唐風 閱讀(1862) 評論(14)  編輯 收藏 引用 所屬分類: 語言技術

評論

# re: 與臨時對象的斗爭(下) 2009-12-04 00:22 OwnWaterloo
哦…… 原來這種技術叫expression template……

其實我心里一直這么叫它的:operation proxy……
  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-04 12:35 陳梓瀚(vczh)
不能解決問題嘛,假如我
MyVec a,b,c,d;
d=a+(b+c);
結果還是產(chǎn)生了跟以前一樣多的臨時對象(好像少了一個?但還是很多,如果列表夠長)

這么設計ExpPlus其實是錯誤的。  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-04 12:36 陳梓瀚(vczh)
@陳梓瀚(vczh)
這個錯誤也造成了你需要ExpPlus<ExpPlus<...<MyVec>...>>這種囧類型了。你有沒有想過讓ExpPlus<I>+ExpPlus<I>==ExpPlus<I>?  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-04 18:26 YESHG!
俺來支持下。
二元操作符相當于一個函數(shù),函數(shù)返回需要生成臨時對象,如果臨時對象過大將產(chǎn)生效率問題。ET用可以忽略不計的臨時對象代替大的臨時對象,將多個二元操作封到了一個函數(shù)中,減少了許多過大的臨時對象的產(chǎn)生。
假如MyVec的構造函數(shù)中不分配那么多空間的話,其實這個開銷還好,呵呵。對象過大的話,一般使用指針去更改對象內(nèi)具體的成員了,而不是整個對象作為參數(shù)傳過來傳過去,當然,這樣可能沒后者直觀……  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-04 18:35 YESHG!
雖然沒有產(chǎn)生很多臨時的MyVec,但是產(chǎn)生了很多臨時的int,函數(shù)調(diào)用的次數(shù)也多了幾次,這里就是用int換MyVec的做法。
俺發(fā)現(xiàn)俺還是不喜歡用模板啊,看了就暈乎。  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-04 18:41 唐風
@陳梓瀚(vczh)
嗯嗯,好敏銳啊,你說得很對。
d=a+(b+c);
這種在這段程序里是會產(chǎn)生編譯錯誤的……

這個模板類確實非常的不完善,對于一個“可用”的ExpXXX的話,應該要是左右操作數(shù)都需要泛化的。為了進一步的重用,可以再泛化操作類型(加減乘)。

這篇文章里我想把重點放在“它可以消除臨對象上”,為了能更“簡單”地說明ET,所以寫的例子很簡陋,不過我想大概的做法已經(jīng)點到了。

完善的做法比較復雜,所以我認為這個庫設計都用的武器,寫應用層邏輯時也許不太會用到?(調(diào)試、維護都不容易啊……)

下面有一篇文章,從頭開始一點一點地完善這個ExpXXX,講解得非常細~,每一個改善是為了什么!(不過是日文的,嘿,YESHG,為你準備的!中文英文的應該也有,不過我沒搜到這么細致的,呵呵,不過對《C++templates》里的例子應該也夠看了。)
http://homepage1.nifty.com/herumi/prog/prog81.html#MOTIVATION

PS:
至于那個囧類型,我還沒看到有什么好的做法。因為要讓左右操作數(shù)接受不同的表達式(a,a+b,a+b*c等等等等)才可用,所以操作數(shù)的“類型”不能固定,ExpPlus<I>+ExpPlus<I>==ExpPlus<I>,感覺行不通。你有什么好做法呢?

  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-04 18:49 唐風
@YESHG!
謝謝老大支持!!

配合內(nèi)聯(lián) RVO,很多情況下這些臨時小對象也可以消失,你看看我上面回復中給的鏈接,MS 有說明……

至于不習慣模板,嘿嘿,吐啊吐啊就習慣了。:)

  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-04 18:51 唐風
@OwnWaterloo

operation proxy……

好形象啊,哈哈
你上篇的回復里就說到了這個呢,不過我是事后才認識到……:P

  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-07 11:36 Wang Feng
ans = a+b+c+d+e+f+....;
如果可以并行的話,這樣很有優(yōu)勢
ans = ((a+b)+(c+d))+((e+f)+(g+h));
你這個+=雖然省去了一些臨時對象,不過也只好一個一個乖乖地+=了,單個cpu的時候沒有事情,多個cpu的時候,大好時間都浪費了。  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-07 19:24 唐風
@Wang Feng
確實如你所言。謝謝你的回復。

說實話,“并行計算”這種高級貨,我還真沒玩過,嘿嘿。

嗯嗯,真心求教:
這方面有沒有比較好的已經(jīng)成熟的做法(庫?)?能不能介紹一下?如果一個表達式比較復雜(比如有加減乘、有括號之類)有什么算法能正確地最優(yōu)地根據(jù)可以并行的“行程”數(shù)來分配計算子過程?各子過程的之間的“通信”是怎么做的呢?會像一般“多線程”中的鎖機制那樣嗎(當然,我對鎖這東西的理解也限于“理論”范疇內(nèi),呵呵)?

PS:
剛才回訪了你的 blog,里面大多都是算法方面的東西啊,呵呵。向你學習 :D




  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-08 16:50 陳梓瀚(vczh)
竟然被你發(fā)現(xiàn)我的百度宅博  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2009-12-08 18:15 唐風
@陳梓瀚(vczh)
好吧,我只能說,是你自己在 cppblog 的鏈接中列出來的。哈哈哈哈 :P
閣下愛好很廣嘛,編譯原理狂人(別人這么叫你的) + 漫迷 ……

嗯嗯,凡有回復必回訪是我的原則,哈哈,也是想通這這種途徑了解對方,多交些朋友



  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2013-11-17 00:09 ligand
最后,當進行 d = t2 的時候,MyVec 的賦值運算符被調(diào)用(用 t2 作參數(shù))。
======================
這句話是說的。這里調(diào)用的不是賦值運算符,而是拷貝構造函數(shù)。  回復  更多評論
  

# re: 與臨時對象的斗爭(下) 2013-11-17 00:12 ligand
表達式模板,看起來很美:通過保存對操作數(shù)(operand)的引用,避免了構造龐大的臨時對象(如涉及大量的資源、動態(tài)內(nèi)存的獲取)。 的確效率提高了。  回復  更多評論
  

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            香蕉久久国产| 欧美影片第一页| 久久精品亚洲一区| 亚洲专区免费| 亚洲欧美日韩国产| 久久精品国产2020观看福利| 午夜精品999| 久久精品国产一区二区电影 | 国产女精品视频网站免费| 欧美午夜视频| 国产日韩三区| 亚洲国产精品成人综合| 亚洲最快最全在线视频| 亚洲欧美日本精品| 久久久久高清| 亚洲精品国产拍免费91在线| 亚洲欧洲日韩女同| 亚洲午夜电影在线观看| 久久精品女人| 欧美午夜性色大片在线观看| 好吊成人免视频| 一本一本久久a久久精品综合妖精| 性欧美xxxx大乳国产app| 老牛嫩草一区二区三区日本| 一本色道久久综合亚洲精品不| 久久精品观看| 国产精品成人一区二区三区吃奶| 一区二区三区在线视频免费观看| 一区二区三区视频在线| 久久综合中文字幕| 亚洲一区二区三区在线视频| 免费高清在线一区| 国产欧美日韩一区二区三区| 99re66热这里只有精品4| 久久亚洲不卡| 亚洲一区二区在线免费观看视频| 老司机精品导航| 国产欧美日韩中文字幕在线| 一区二区三区高清| 欧美激情一区二区三区| 欧美一区二区视频97| 欧美日韩国产精品 | 久久欧美中文字幕| 一本色道久久加勒比精品| 巨乳诱惑日韩免费av| 国产日韩精品一区二区三区在线| 一区二区日本视频| 亚洲黑丝一区二区| 久久综合精品一区| 国内成人精品一区| 久久精品色图| 欧美亚洲综合在线| 国产情人节一区| 亚洲男同1069视频| 国产精品久久久久免费a∨大胸| 国产在线拍揄自揄视频不卡99| 亚洲视频香蕉人妖| 亚洲国产一区二区三区在线播| 久久九九电影| 国产午夜精品理论片a级探花| 亚洲欧美在线一区| 中文精品99久久国产香蕉| 欧美三级午夜理伦三级中视频| 日韩视频免费| 亚洲精品乱码久久久久久黑人 | 亚洲人体影院| 欧美日韩国产一中文字不卡| 亚洲毛片一区| 亚洲美女视频| 国产精品久久久久久久电影| 亚洲欧美日韩在线高清直播| 亚洲欧美久久久| 国产一区二区久久久| 久久综合狠狠综合久久综合88| 久久国产精品高清| 亚洲激情av| 99综合在线| 国产日韩在线一区二区三区| 久久综合色影院| 蜜臀久久久99精品久久久久久| 亚洲国产视频a| 一区二区福利| 狠狠做深爱婷婷久久综合一区| 免费看成人av| 欧美日韩高清不卡| 欧美亚洲三级| 美日韩精品视频| 亚洲一二三四久久| 欧美在线日韩精品| 亚洲精品影视| 羞羞视频在线观看欧美| 亚洲福利视频二区| 亚洲视频在线观看三级| 精品91免费| 一区二区高清在线观看| 狠狠色丁香婷婷综合| 亚洲免费大片| 韩国一区二区三区在线观看| 亚洲日本在线视频观看| 国产日韩欧美一区二区| 亚洲风情亚aⅴ在线发布| 欧美日韩在线视频一区| 久久精品理论片| 欧美日韩国产精品一区| 久久青青草原一区二区| 欧美亚州一区二区三区| 免费成人黄色av| 国产精品免费在线| 亚洲国产你懂的| 尤物九九久久国产精品的特点| 日韩午夜av电影| 亚洲高清视频的网址| 亚洲高清自拍| 国产精品视频在线观看| 亚洲国产日韩欧美综合久久| 国产一区二区欧美日韩| 亚洲免费观看视频| 亚洲国产精品va在线看黑人 | 久久久噜噜噜久久| 亚洲欧美日韩第一区| 欧美女主播在线| 欧美激情国产日韩精品一区18| 国产精品国产亚洲精品看不卡15| 亚洲国产色一区| 影视先锋久久| 性做久久久久久久久| 亚洲在线播放电影| 欧美日韩一区二区欧美激情| 欧美大片91| 激情综合色综合久久综合| 亚洲免费视频观看| 亚洲欧美日韩天堂| 欧美日韩大片一区二区三区| 亚洲国产成人久久综合| 亚洲日本欧美日韩高观看| 美女网站在线免费欧美精品| 麻豆久久精品| 亚洲国产精品成人综合| 欧美不卡在线视频| 亚洲高清不卡| avtt综合网| 欧美日韩在线电影| 在线中文字幕一区| 午夜在线视频观看日韩17c| 欧美视频在线不卡| 一区二区欧美在线| 欧美一区二区三区免费观看| 国产精品一二三四区| 午夜国产精品视频| 久久久www成人免费无遮挡大片| 国产在线精品一区二区夜色| 久久久久久网址| 亚洲第一中文字幕| 99亚洲一区二区| 欧美视频在线观看免费网址| 亚洲视频一区二区| 久久国产精品免费一区| 1000部国产精品成人观看| 欧美国产日韩精品| 在线综合欧美| 久久天天躁狠狠躁夜夜av| 亚洲国产欧美一区二区三区同亚洲| 欧美不卡在线视频| 亚洲午夜精品网| 嫩草国产精品入口| 亚洲性线免费观看视频成熟| 国产欧美69| 欧美成人在线网站| 亚洲一区中文| 欧美激情区在线播放| 亚洲欧美成人一区二区在线电影| 国产字幕视频一区二区| 欧美久久一区| 久久成人精品| 亚洲毛片视频| 老色鬼精品视频在线观看播放| 亚洲精品在线免费观看视频| 国产精品久久久一区二区三区| 久久精品国产77777蜜臀| 国产精品电影观看| 亚洲欧美综合| 免费日韩成人| 美国十次了思思久久精品导航| 欧美成人午夜激情在线| 久久免费午夜影院| 亚洲社区在线观看| 亚洲国产精品热久久| 亚欧美中日韩视频| 99精品欧美一区二区三区| 韩国av一区二区三区在线观看| 欧美精品一区二区三| 久久一区二区三区超碰国产精品| 亚洲一区二区毛片| 亚洲精品影院| 亚洲精华国产欧美| 免费视频亚洲| 久久亚洲精品一区二区| 性欧美精品高清| 亚洲综合国产激情另类一区| 亚洲精品日韩在线|