• <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>

            MyMSDN

            MyMSDN記錄開發(fā)新知道

            [轉(zhuǎn)]從C++的Return Value Optimization (RVO)到C#的value type

            先看一段簡(jiǎn)單的C++代碼:

            Type get(int I){
                return Type(i);
            } 
            
            Type t = get(1); 

            這里, 我們從C++的基本語義看上去, 應(yīng)該是Type(i) 調(diào)用一次拷貝構(gòu)造函數(shù), 在堆棧中生成一個(gè)臨時(shí)對(duì)象;然后,用該對(duì)象構(gòu)造返回對(duì)象;然后對(duì)這個(gè)臨時(shí)對(duì)象調(diào)用析構(gòu)函數(shù);在調(diào)用者方, 用返回的臨時(shí)對(duì)象調(diào)用拷貝構(gòu)造函數(shù)以初始化對(duì)象t, 返回對(duì)象的析構(gòu)函數(shù)在這之后, 函數(shù)返回之前調(diào)用。

            所以, Type t = get(i); 應(yīng)該有三個(gè)拷貝構(gòu)造函數(shù)和兩個(gè)析構(gòu)函數(shù)的調(diào)用.

            可是, 還有一種說法是, 編譯器可能會(huì)對(duì)這兩個(gè)臨時(shí)對(duì)象進(jìn)行優(yōu)化,最終的優(yōu)化結(jié)果會(huì)是只有一次的構(gòu)造函數(shù)。因?yàn)楹苊黠@地可以看到, 這里我們其實(shí)只是要用一個(gè)整數(shù)構(gòu)造一個(gè)Type對(duì)象。

            嗯. 似乎很有道理!

            那么, 哪一種說法對(duì)呢? 沒有調(diào)查就沒有發(fā)言權(quán),于是本人用VC++6.0做了實(shí)驗(yàn)。 放了些cout<<…..在拷貝構(gòu)造函數(shù)里,觀察打印的結(jié)果, 結(jié)果卻是跟我的simple, na?ve的預(yù)測(cè)一致。三個(gè)拷貝構(gòu)造函數(shù), 兩個(gè)析構(gòu)函數(shù)。

            “你個(gè)弱智編譯器!腦袋進(jìn)水了吧?”(忘了編譯器沒腦袋了)“很明顯在這個(gè)例子里我的兩個(gè)臨時(shí)對(duì)象都沒有用的啊!”

            于是,上網(wǎng), 查資料, google一下吧!

            下面是我查到的一些結(jié)果:

            其實(shí), 這種對(duì)值傳遞的優(yōu)化的研究, 并不只局限于返回值。對(duì)下面這個(gè)例子:

            void f(T t) { } 
            void main(void){ 
                T t1;
                f(t1); 
            } 

            也有這種考慮。

            f(T)是按值傳遞的。語義上應(yīng)該做一個(gè)復(fù)制, 使得函數(shù)內(nèi)部對(duì)T的改變不會(huì)影響到原來的t1.

            但是,因?yàn)樵谡{(diào)用f(t1)之后, 我們沒有再使用t1(除了一個(gè)隱含的destructor調(diào)用),是否可能把復(fù)制優(yōu)化掉, 直接使用t1呢?這樣可以節(jié)省掉一個(gè)拷貝構(gòu)造函數(shù)和一個(gè)析構(gòu)函數(shù)。

            可是, 不論是對(duì)返回值的優(yōu)化, 還是對(duì)上面這種局部對(duì)象的優(yōu)化,在1995年的C++新標(biāo)準(zhǔn)草案出臺(tái)前都是為標(biāo)準(zhǔn)所嚴(yán)格限制的 (雖然有些編譯器并沒有遵行這個(gè)標(biāo)準(zhǔn), 還是支持了這種“優(yōu)化”)

            那么, 這又是為什么呢?

            這里面涉及到一個(gè)普遍的對(duì)side-effect的擔(dān)憂。

            什么又是side-effect呢?

            所謂side-effect就是一個(gè)函數(shù)的調(diào)用與否能夠?qū)ο到y(tǒng)的狀態(tài)造成區(qū)別。

            int add(int i, int j){ return i+j; }就是沒有side-effect的,而

            void set(int* p, int I, int v){ p[I]=v; }就是有side-effect的。因?yàn)樗淖兞艘粋€(gè)數(shù)組元素的值, 而這個(gè)數(shù)組元素在函數(shù)外是可見的。

            通常意義上來說, 所有的優(yōu)化應(yīng)該在不影響程序的可觀察行為的基礎(chǔ)上進(jìn)行的。否則,快則快了, 結(jié)果卻和所想要的完全不同!

            而C++的拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)又很多都是有side-effect的。如果我們的“優(yōu)化”去掉了一個(gè)有side-effect的拷貝構(gòu)造函數(shù)和一個(gè)析構(gòu)函數(shù), 這個(gè)“優(yōu)化”就有可能改變程序的可觀察行為。(注意, 我這里說的是“可能”,因?yàn)椤柏?fù)負(fù)得正”, 兩個(gè)有side-effect的函數(shù)的調(diào)用, 在不考慮并行運(yùn)行的情況下, 也許反而不會(huì)影響程序的可觀察行為。不過, 這種塞翁失馬的事兒, 編譯器就很難判斷了)

            基于這種憂慮, 1995年以前的標(biāo)準(zhǔn), 明確禁止對(duì)含有side-effect的拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)的優(yōu)化。同時(shí), 還有一些對(duì)C++擴(kuò)充的提議, 考慮讓程序員自己對(duì)類進(jìn)行允許優(yōu)化的聲明。 程序員可以明確地告訴編譯器:不錯(cuò), 我這個(gè)拷貝構(gòu)造函數(shù), 析構(gòu)函數(shù)是有side-effect, 但你別管, 盡管優(yōu)化, 出了事有我呢!

            哎, side-effect真是一個(gè)讓人又恨又愛的東西!它使編譯器的優(yōu)化變得困難;加大了程序維護(hù)和調(diào)試的難度。因此 functional language 把side-effect當(dāng)作洪水猛獸一樣,干脆禁止。但同時(shí),我們又很難離開side-effect. 不說程序員們更習(xí)慣于imperative 的編程方法, 象數(shù)據(jù)庫操作,IO操作都天然就是side-effect.

            不過,個(gè)人還是認(rèn)為C++標(biāo)準(zhǔn)對(duì)“優(yōu)化”的保守態(tài)度是有道理的。無論如何,讓“優(yōu)化”可以潛在地偷偷地改變程序的行為總是讓人想起來就不舒服的。

            但是, 矛盾是對(duì)立統(tǒng)一的。(想當(dāng)年俺馬列可得了八十多分呢)。 對(duì)這種aggressive的“優(yōu)化”的呼聲是一浪高過一浪。 以Stan Lippeman為首的一小撮頑固分子對(duì)標(biāo)準(zhǔn)的顛覆和和平演變的陰謀從來就沒有停止過。 這不?在1996年的一個(gè)風(fēng)雨交加的夜晚, 一個(gè)陰險(xiǎn)的C++新標(biāo)準(zhǔn)草案出爐了。在這個(gè)草案里, 加入了一個(gè)名為RVO (Return Value Optimization) 的放寬對(duì)優(yōu)化的限制, 妄圖走資本主義道路, 給資本家張目的提案。其具體內(nèi)容就是說:允許編譯器對(duì)命名過的局部對(duì)象的返回進(jìn)行優(yōu)化, 即使拷貝構(gòu)造函數(shù)/析構(gòu)函數(shù)有side-effect也在所不惜。這個(gè)提議背后所隱藏的思想就是:為了提高效率, 寧可冒改變程序行為的風(fēng)險(xiǎn)。寧要資本主義的苗, 不要社會(huì)主義的草了!

            我想, 這樣的一個(gè)罪大惡極的提案竟會(huì)被提交,應(yīng)該是因?yàn)镃++的值拷貝的語義的效率實(shí)在太“媽媽的”了。 當(dāng)你寫一個(gè) Complex operator+(const Complex& c1, const Complex& c2);的時(shí)候, 竟需要調(diào)用好幾次拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)!同志們!(沉痛地, 語重心長(zhǎng)地)社會(huì)主義的生產(chǎn)關(guān)系的優(yōu)越性怎么體現(xiàn)啊?

            接下來, 當(dāng)我想Google C++最新的標(biāo)準(zhǔn), 看RVO是否被最終采納時(shí), 卻什么也找不到了。 到ANSI的網(wǎng)站上去, 居然要付錢才能DOWNLOAD文檔。 “老子在城里下館子都不付錢, down你幾個(gè)爛文檔還要給錢?!”

            故事沒有結(jié)局, 實(shí)在是不爽。 也不知是不是因?yàn)闃?biāo)準(zhǔn)還沒有敲定, 所以VC++6 就沒有優(yōu)化, 還是VC根本就沒完全遵守標(biāo)準(zhǔn)。

            不過,有一點(diǎn)是肯定的。 當(dāng)寫程序的時(shí)候, 最好不要依賴于RVO (有人, 象Stan Lippeman, 又叫它NRV優(yōu)化)。 因?yàn)椋?不論對(duì)標(biāo)準(zhǔn)的爭(zhēng)論是否已經(jīng)有了結(jié)果, 實(shí)際上各個(gè)編譯器的實(shí)現(xiàn)仍還是各自為政, 沒有統(tǒng)一。 一個(gè)叫SCOtt Meyers的家伙(忘了是賣什么的了)就說, 如果你的程序依賴于RVO, 最好去掉這種依賴。也就是說, 不管RVO到底標(biāo)準(zhǔn)不標(biāo)準(zhǔn), 你還是不能用。 不僅不能用, 還得時(shí)刻警惕著RVO可能帶來的程序行為上的變化。 (也不知這幫家伙瞎忙了半天到底為啥!)

            說到這里, 倒想起了C#里一個(gè)困惑了我很久的問題。記得讀C#的specification的時(shí)候, 非常不解為什么C#不允許給value type 定義析構(gòu)函數(shù)。

            這里, 先簡(jiǎn)略介紹一下C#里的value type (原始數(shù)據(jù)類型, struct 類型)。

            在C#里的value_type就象是值, 永遠(yuǎn)只能copy, 取值。因此, 它永遠(yuǎn)是in-place的。如果你把一個(gè)value type的數(shù)據(jù)放在一個(gè)對(duì)象里,它的生命期就和那個(gè)對(duì)象相同;如果你聲明一個(gè)value type 的變量在函數(shù)中, 它的生命期就在lexical scope里。

            {

              The_ValueType value;

            }//value 到這里就死菜了

            啊呀呀! 這不正是我們懷念的C++的stack object嗎?

            在C++里,Auto_ptr, shared_ptr, 容器們, 不都是利用析構(gòu)函數(shù)來管理資源的嗎?

            C#,Java 雖然利用garbage collection技術(shù)來收集無用對(duì)象, 使我們不用再擔(dān)心內(nèi)存的回收。 但garbage collection并不保證無用對(duì)象一定被收集, 并不保證Dispose()函數(shù)一定被調(diào)用, 更不保證一個(gè)對(duì)象什么時(shí)候被回收。 所以對(duì)一些非內(nèi)存的資源, 象數(shù)據(jù)庫連接, 網(wǎng)絡(luò)連接, 我們還是希望能有一個(gè)類似于smart pointer的東西來幫我們管理啊。(try-finally 雖然可以用, 但因?yàn)樗绊懙絣exical scope, 有時(shí)用起來不那么方便)

            于是, 我對(duì)C#的取消value type的析構(gòu)函數(shù)充滿了深厚的階級(jí)仇恨。

            不過, 現(xiàn)在想來, C#的這種設(shè)計(jì)一定是懲于C++失敗的教訓(xùn):

               1. value type 沒有拷貝構(gòu)造函數(shù)。C#只做缺省copy, 沒有side-effect
               2. value type 不準(zhǔn)有析構(gòu)函數(shù)。C#有g(shù)arbage collection, 析構(gòu)函數(shù)的唯一用途只會(huì)是做一些side-effect象關(guān)閉數(shù)據(jù)庫連接。 所以取消了析構(gòu)函數(shù), 就取消了value type的side-effect.
               3. 沒有了side-effect, 系統(tǒng)可以任意地做優(yōu)化了

            對(duì)以下程序:

            The_Valuetype get(int I){return The_Valuetype(i);}

            The_Valuetype t = get(1);

            在C#里我們可以快樂地說:只調(diào)用了一次構(gòu)造函數(shù)。 再?zèng)]有side-effect的沙漠, 再?zèng)]有難以優(yōu)化的荒原, smart pointer望而卻步, 效率之花處處開遍。 I have a dream, ……

            轉(zhuǎn)載自:http://gugu99.itpub.net/post/34143/466008

            posted on 2010-04-06 19:42 volnet 閱讀(587) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 知識(shí)庫(KnowledgeLibrary)C/C++

            特殊功能
             
            久久无码中文字幕东京热| 久久久久女教师免费一区| 久久最新免费视频| 久久国产视频网| 久久久久国产精品麻豆AR影院| 国产精品无码久久久久| 久久国产美女免费观看精品| 久久久久亚洲av成人无码电影| 国内精品久久久久久麻豆| 性做久久久久久久久| 少妇熟女久久综合网色欲| 久久精品国产亚洲av麻豆图片 | 色噜噜狠狠先锋影音久久| 亚洲国产精品久久久久婷婷软件| 久久福利青草精品资源站免费| 热99re久久国超精品首页| 久久精品国产精品亜洲毛片| 综合久久一区二区三区| 久久久久久久97| 久久精品免费大片国产大片| 99精品久久精品一区二区| 久久电影网2021| 久久婷婷五月综合97色直播| 日韩久久久久久中文人妻| 久久精品国产只有精品2020| 理论片午午伦夜理片久久| 久久久www免费人成精品| 国产一久久香蕉国产线看观看| 国产激情久久久久影院老熟女免费| 欧美午夜A∨大片久久| 热re99久久精品国99热| 国内精品久久久久久麻豆| 日韩AV无码久久一区二区| 国产亚洲精久久久久久无码AV| 亚洲va久久久噜噜噜久久| 久久久精品波多野结衣| 国产午夜免费高清久久影院| 一级a性色生活片久久无| 精品国产福利久久久| 久久久久久国产精品无码下载| 一本一道久久精品综合|