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

woaidongmao

文章均收錄自他人博客,但不喜標題前加-[轉貼],因其丑陋,見諒!~
隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
數據加載中……

C++箴言:理解inline化的介入和排除

  inline 函數——多么棒的主意啊!它們看起來像函數,它們產生的效果也像函數,它們在各方面都比宏好得太多太多,而你卻可以在調用它們時不招致函數調用的成本。你還有什么更多的要求呢?

  實際上你得到的可能比你想的更多,因為避免函數調用的成本只是故事的一部分。在典型情況下,編譯器的優化是為了一段連續的沒有函數調用的代碼設計的,所以當你 inline 化一個函數,你可能就使得編譯器能夠對函數體實行上下文相關的特殊優化。大多數編譯器都不會對 "outlined" 函數調用實行這樣的優化。

  然而,在編程中,就像在生活中,沒有免費午餐,而 inline 函數也不例外。一個 inline 函數背后的思想是用函數本體代替每一處對這個函數的調用,而且不必拿著統計表中的 Ph.D. 就可以看出這樣可能會增加你的目標代碼的大小。在有限內存的機器上,過分熱衷于 inline 化會使得程序對于可用空間來說過于龐大。即使使用了虛擬內存,inline 引起的代碼膨脹也會導致附加的分頁調度,減少指令緩存命中率,以及隨之而來的性能損失。

  在另一方面,如果一個 inline 函數本體很短,為函數本體生成的代碼可能比為一個函數調用生成的代碼還要小。如果是這種情況,inline 化這個函數可以實際上導致更小的目標代碼和更高的指令緩存命中率! 記住,inline 是向編譯器發出的一個請求,而不是一個命令。這個請求能夠以顯式的或隱式的方式提出。隱式的方法就是在一個類定義的內部定義一個函數:

class Person {
 public:
  ...
  int age() const { return theAge; } // an implicit inline request: age is
  ... // defined in a class definition

 private:
  int theAge;
};

  這樣的函數通常是成員函數,不過我們知道友元函數也能被定義在類的內部,如果它們在那里,它們也被隱式地聲明為 inline。

  顯式的聲明一個 inline 函數的方法是在它的聲明之前加上 inline 關鍵字。例如,以下就是標準 max 模板(來自 <algorithm>)經常用到的的實現方法:

template<typename T> // an explicit inline
inline const T& std::max(const T& a, const T& b) // request: std::max is
{ return a < b ? b : a; } // preceded by "inline"

  max 是一個模板的事實引出一個觀察結論:inline 函數和模板一般都是定義在頭文件中的。這就使得一些程序員得出結論斷定函數模板必須是 inline。這個結論是非法的而且有潛在的危害,所以它值得我們考察一下。 inline 函數一般必須在頭文件內,因為大多數構建環境在編譯期間進行 inline 化。為了用被調用函數的函數本體替換一個函數調用,編譯器必須知道函數看起來像什么樣子。(有一些構建環境可以在連接期間進行 inline 化,還有少數幾個——比如,基于 .NET Common Language Infrastructure (CLI) 的控制環境——居然能在運行時 inline 化。然而,這些環境都是例外,并非規則。inline 化在大多數 C++ 程序中是一個編譯時行為。)

  模板一般在頭文件內,因為編譯器需要知道一個模板看起來像什么以便用到它時對它進行實例化。(同樣,也不是全部如此。一些構建環境可以在連接期間進行模板實例化。然而,編譯期實例化更為普遍。) 模板實例化與 inline 化無關。如果你寫了一個模板,而且你認為所有從這個模板實例化出來的函數都應該是 inline 的,那么就聲明這個模板為 inline,這就是上面的 std::max 的實現被做的事情。但是如果你為沒有理由要 inline 化的函數寫了一個模板,就要避免聲明這個模板為 inline(無論顯式的還是隱式的)。inline 化是有成本的,而且你不希望在毫無預見的情況下遭遇它們。我們已經說到 inline 化是如何引起代碼膨脹的,但是,還有其它的成本,過一會兒我們再討論。

  在做這件事之前,我們先來完成對這個結論的考察:inline 是一個編譯器可能忽略的請求。大多數編譯器拒絕它們認為太復雜的 inline 函數(例如,那些包含循環或者遞歸的),而且,除了最細碎的以外的全部虛擬函數的調用都不會被 inline 化。不應該對這后一個結論感到驚訝。虛擬意味著“等待,直到運行時才能斷定哪一個函數被調用”,而 inline 意味著“執行之前,用被調用函數取代調用的地方”。如果編譯器不知道哪一個函數將被調用,你很難責備它們拒絕 inline 化這個函數本體。

  所有這些加在一起,得出:一個被指定的 inline 函數是否能真的被 inline 化,取決于你所使用的構建環境——主要是編譯器。幸運的是,大多數編譯器都有一個診斷層次,在它們不能 inline 化一個你提出的函數時,會導致一個警告。

  有時候,即使當編譯器完全心甘情愿地 inline 化一個函數,他們還是會為這個 inline 函數生成函數本體。例如,如果你的程序要持有一個 inline 函數的地址,編譯器必須為它生成一個 outlined 函數本體。他們怎么能生成一個指向根本不存在的函數的指針呢?再加上,編譯器一般不會對通過函數指針的調用進行 inline 化,這就意味著,對一個 inline 函數的調用可能被也可能不被 inline 化,依賴于這個調用是如何做成的:

inline void f() {...} // assume compilers are willing to inline calls to f

void (*pf)() = f; // pf points to f
...

f(); // this call will be inlined, because it’s a "normal" call
pf(); // this call probably won’t be, because it’s through
// a function pointer

  甚至在你從來沒有使用函數指針的時候,未 inline 化的 inline 函數的幽靈也會時不時地拜訪你,因為程序員并不必然是函數指針的唯一需求者。有時候編譯器會生成構造函數和析構函數的 out-of-line 拷貝,以便它們能得到指向這些函數的指針,在對數組中的對象進行構造和析構時使用。

  事實上,構造函數和析構函數對于 inline 化來說經常是一個比你在不經意的檢查中所能顯示出來的更加糟糕的候選者。例如,考慮下面這個類 Derived 的構造函數:

class Base {
 public:
  ...

 private:
  std::string bm1, bm2; // base members 1 and 2
};

class Derived: public Base {
 public:
  Derived() {} // Derived’s ctor is empty - or is it?
  ...

 private:
  std::string dm1, dm2, dm3; // derived members 1-3
};

  這個構造函數看上去像一個 inline 化的極好的候選者,因為它不包含代碼。但是視覺會被欺騙。

  C++ 為對象被創建和被銷毀時所發生的事情做出了各種保證。例如,當你使用 new 時,你的動態的被創建對象會被它們的構造函數自動初始化,而當你使用 delete。則相應的析構函數會被調用。當你創建一個對象時,這個對象的每一個基類和每一個數據成員都會自動構造,而當一個對象被銷毀時,則發生關于析構的反向過程。如果在一個對象構造期間有一個異常被拋出,這個對象已經完成構造的任何部分都被自動銷毀。所有這些情節,C++ 只說什么必須發生,但沒有說如何發生。那是編譯器的實現者的事,但顯然這些事情不會自己發生。在你的程序中必須有一些代碼使這些事發生,而這些代碼——由編譯器寫出的代碼和在編譯期間插入你的程序的代碼——必須位于某處。有時它們最終就位于構造函數和析構函數中,所以我們可以設想實現為上面那個聲稱為空的 Derived 的構造函數生成的代碼就相當于下面這樣:

Derived::Derived() // conceptual implementation of
{
 // "empty" Derived ctor

 Base::Base(); // initialize Base part

 try { dm1.std::string::string(); } // try to construct dm1
 catch (...) { // if it throws,
  Base::~Base(); // destroy base class part and
 throw; // propagate the exception
}

try { dm2.std::string::string(); } // try to construct dm2
catch(...) {
 // if it throws,
 dm1.std::string::~string(); // destroy dm1,
 Base::~Base(); // destroy base class part, and
throw; // propagate the exception
}

try { dm3.std::string::string(); } // construct dm3
catch(...) {
 // if it throws,
 dm2.std::string::~string(); // destroy dm2,
 dm1.std::string::~string(); // destroy dm1,
 Base::~Base(); // destroy base class part, and
throw; // propagate the exception
}
}

  這些代碼并不代表真正的編譯器所生成的,因為真正的編譯器會用更復雜的方法處理異常。盡管如此,它還是準確地反映了 Derived 的“空”構造函數必須提供的行為。不論一個編譯器的異常多么復雜,Derived 的構造函數至少必須調用它的數據成員和基類的構造函數,而這些調用(它們自己也可能是 inline 的)會影響它對于 inline 化的吸引力。

  同樣的原因也適用于 Base 的構造函數,所以如果它是 inline 的,插入它的全部代碼也要插入 Derived 的構造函數(通過 Derived 的構造函數對 Base 的構造函數的調用)。而且如果 string 的構造函數碰巧也是 inline 的,Derived 的構造函數中將增加五個那個函數代碼的拷貝,分別對應于 Derived 對象中的五個 strings(兩個繼承的加上三個它自己聲明的)。也許在現在,為什么說是否 inline 化 Derived 的構造函數不是一個不經大腦的決定就很清楚了。類似的考慮也適用于 Derived 的析構函數,用同樣的或者不同的方法,必須保證所有被 Derived 的構造函數初始化的對象被完全銷毀。

  庫設計者必須評估聲明函數為 inline 的影響,因為為庫中的客戶可見的 inline 函數提供二進制升級版本是不可能的。換句話說,如果 f 是一個庫中的一個 inline 函數,庫的客戶將函數 f 的本體編譯到他們的應用程序中。如果一個庫的實現者后來決定修改 f,所有使用了 f 的客戶都必須重新編譯。這常常會令人厭煩。在另一方面,如果 f 是一個非 inline 函數,對 f 的改變只需要客戶重新連接。這與重新編譯相比顯然減輕了很大的負擔,而且,如果庫中包含的函數是動態鏈接的,這就是一種對于用戶來說完全透明的方法。
 
  為了程序開發的目標,在頭腦中牢記這些需要考慮的事項是很重要的,但是從編碼期間的實用觀點來看,占有支配地位的事實是:大多數調試器會與 inline 函數發生沖突。這不應該是什么重大的發現。你怎么能在一個不在那里的函數中設置斷點呢?雖然一些構建環境設法支持 inline 函數的調試,多數環境還是簡單地為調試構建取消了 inline 化。

  這就導出了一個用于決定哪些函數應該被聲明為 inline,哪些不應該的合乎邏輯的策略。最初,不要 inline 任何東西,或者至少要將你的 inline 化的范圍限制在那些必須 inline 的和那些實在微不足道的函數上。通過慎重地使用 inline,你可以使調試器的使用變得容易,但是你也將 inline 化放在了它本來應該在的地位:作為一種手動的優化。不要忘記由經驗確定的 80-20 規則,它宣稱一個典型的程序用 80% 的時間執行 20% 的代碼。這是一個重要的規則,因為它提醒你作為一個軟件開發者的目標是識別出能全面提升你的程序性能的 20% 的代碼。你可以 inline 或者用其他方式無限期地調節你的函數,但除非你將精力集中在正確的函數上,否則就是白白浪費精力。

  Things to Remember

  ·將大部分 inline 限制在小的,調用頻繁的函數上。這使得程序調試和二進制升級更加容易,最小化潛在的代碼膨脹,并最大化提高程序速度的幾率。

  ·不要僅僅因為函數模板出現在頭文件中,就將它聲明為 inline。

posted on 2008-07-16 13:27 肥仔 閱讀(170) 評論(0)  編輯 收藏 引用 所屬分類: C++ 基礎

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲精品国产精品久久清纯直播 | 久久久之久亚州精品露出| 亚洲第一在线视频| 国产一区二区成人久久免费影院| 欧美视频中文字幕| 国产精品久久精品日日| 欧美先锋影音| 国产日韩精品电影| 国产一区二区三区的电影| 国产在线播精品第三| 在线播放日韩欧美| 亚洲精品黄色| 亚洲视屏在线播放| 欧美在线精品免播放器视频| 欧美中文在线观看| 榴莲视频成人在线观看| 欧美+亚洲+精品+三区| 亚洲高清资源| 免费亚洲网站| 9色porny自拍视频一区二区| 亚洲一线二线三线久久久| 久久国产乱子精品免费女| 欧美h视频在线| 国产精品日韩精品欧美精品| 在线观看精品一区| 亚洲婷婷综合久久一本伊一区| 久久成人精品电影| 亚洲国产精品综合| 午夜精品久久久久久久99水蜜桃| 久久综合九色综合欧美就去吻 | 国产一区亚洲| 亚洲区一区二| 午夜精品久久久久99热蜜桃导演| 另类天堂av| 亚洲欧美日本国产专区一区| 你懂的亚洲视频| 国产欧美日韩亚洲一区二区三区| 在线看片一区| 亚洲欧美中文另类| 在线视频成人| 亚洲主播在线| 亚洲欧洲午夜| 久久成人人人人精品欧| 欧美日韩另类丝袜其他| 亚洲国产精品va在线观看黑人| 亚洲主播在线观看| 亚洲欧洲日韩综合二区| 欧美在线观看www| 国产精品扒开腿爽爽爽视频| 亚洲日本中文字幕免费在线不卡| 久久精品av麻豆的观看方式| 亚洲视频1区| 欧美α欧美αv大片| 在线观看亚洲精品视频| 性欧美暴力猛交另类hd| 最新国产拍偷乱拍精品 | 乱码第一页成人| 国产伦理一区| 欧美一二区视频| 99国产欧美久久久精品| 欧美大片一区二区三区| 亚洲国产合集| 欧美激情精品| 免费成人性网站| 在线成人欧美| 欧美1区2区3区| 久久激情婷婷| 在线成人小视频| 欧美激情综合| 欧美区在线播放| 亚洲狼人精品一区二区三区| 91久久久国产精品| 欧美激情在线有限公司| 一区二区免费在线观看| 在线视频日本亚洲性| 欧美性理论片在线观看片免费| 宅男在线国产精品| 在线视频免费在线观看一区二区| 国产精品欧美一区二区三区奶水 | 亚洲欧美日韩在线高清直播| 亚洲一区二区三区激情| 国产午夜久久| 毛片av中文字幕一区二区| 老司机午夜精品视频| 亚洲片国产一区一级在线观看| 亚洲国产日韩欧美在线动漫| 欧美日韩中文另类| 欧美一二三区在线观看| 久久精品理论片| 亚洲精品视频一区二区三区| 一区二区三区www| 国产亚洲精品v| 欧美福利电影网| 欧美日韩精品综合| 久久精品一区蜜桃臀影院| 免费av成人在线| 亚洲一区免费看| 久久久久久9999| 欧美在线观看天堂一区二区三区| 亚洲午夜一区| 欧美日韩伦理在线| 欧美一区二区三区男人的天堂| 久久国产主播| 亚洲午夜视频在线| 欧美在线一级视频| 亚洲乱码一区二区| 性色av一区二区三区在线观看 | 国产伦精品一区二区三区| 欧美大片免费| 国产精品老牛| 亚洲人成网站在线播| 黄色成人免费网站| 99视频一区| 亚洲精品一二区| 久久久久国产精品麻豆ai换脸| 亚洲一区二区视频在线观看| 久久综合影音| 久久福利精品| 国产精品免费看久久久香蕉| 亚洲人人精品| 亚洲欧洲在线播放| 久久久噜噜噜久久狠狠50岁| 亚洲午夜久久久久久久久电影网| 久久国产欧美| 性色一区二区三区| 欧美日韩一区二区高清| 亚洲国产日韩欧美一区二区三区| 激情另类综合| 久久国产视频网| 性欧美1819sex性高清| 欧美日韩在线视频一区| 亚洲高清av| 亚洲国产精品黑人久久久| 久久激情五月激情| 久久久久久夜| 国产一区二区三区日韩| 亚洲网站在线看| 亚洲午夜伦理| 欧美性猛交视频| 亚洲午夜av在线| 亚洲综合电影| 国产精品系列在线播放| 亚洲女与黑人做爰| 性色av一区二区三区在线观看| 欧美无砖砖区免费| 99精品国产热久久91蜜凸| 在线中文字幕一区| 欧美午夜精品久久久久久孕妇| 亚洲伦理在线免费看| 一区二区三区欧美在线| 欧美日韩中文在线观看| 亚洲无人区一区| 欧美一区二区三区的| 国产一区欧美日韩| 欧美一级大片在线观看| 久久婷婷久久| 亚洲国产激情| 欧美片在线观看| 午夜精品久久久久久久蜜桃app| 久久精品一区二区三区不卡牛牛| 在线观看亚洲一区| 欧美高清在线一区| 亚洲激情在线观看视频免费| 一本色道久久99精品综合| 艳女tv在线观看国产一区| 欧美日韩你懂的| 亚洲一区二区在线观看视频| 欧美在线日韩精品| 亚洲国产清纯| 欧美日韩一区二区三区四区五区| 在线性视频日韩欧美| 久久精品国产清高在天天线 | 欧美专区在线观看一区| 精品成人在线| 欧美丰满少妇xxxbbb| 99视频精品全国免费| 久久国产免费看| 亚洲国产成人不卡| 欧美日韩中文字幕日韩欧美| 亚洲综合丁香| 欧美成人黄色小视频| 亚洲欧洲一区二区三区| 国产精品久久久久久亚洲毛片| 欧美一区在线视频| 亚洲三级免费| 久久久久久久97| 一区二区高清视频| 国产亚洲美州欧州综合国| 欧美国产一区视频在线观看| 亚洲大胆人体视频| 欧美一区二区三区免费看| 亚洲欧洲视频在线| 国产日韩欧美电影在线观看| 欧美成人一区二区三区| 午夜亚洲福利| 99这里只有久久精品视频| 你懂的国产精品| 欧美专区日韩视频| 亚洲天堂成人| 亚洲美洲欧洲综合国产一区|