程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++ Low level performance optimize 2

C++ Low level performance optimize 2

編輯:C++入門知識

首先,上一篇文章討論cache missing的重要性時,用了list做比較,目的並不是說list沒有用,而是說明cache missing會對性能有重要影響。如果元素不多,並且對象復制的代價很大,那麼list可能就是更好的選擇。其次,這裡討論的大部分是編碼時一些比較底層的技巧,當遇到性能問題時,應該先考慮是否能在高層改進算法,減少運算,實在不行,在考慮這類優化技巧。性能優化的挑戰就在於沒有完美的永遠適用的方案,了解這些技巧讓我們在優化代碼時有更多武器,但最終選擇哪個方案還需要更加實際情況,並且以profile的實際數據為依據來做。   1.  Data Layout   調整數據布局是常見的優化手段,做此類優化時有幾點需要注意:首先是內存占用,現代編譯器默認大多以16或32位對齊,因此   復制代碼 struct BadLayout {          int8_t i0;          int32_t i1;          int8_t i2; };   struct GoodLayout {          int8_t i0;          int8_t i2;          int32_t i1; }; 復制代碼 sizeof(Goodlayout) >= sizeof(BadLayout) 在vs2013默認對齊設置下,BadLayout==12 byte,GoodLayout==8byte。         其次,經常訪問或者相關的數據應該放到一起,減少cache missing。Going native2013 Andrei Alexandrescu介紹了facebook做的重要性能優化就是把php代碼編譯為c++代碼,而在代碼轉換中重要的一步就是根據數據的”hotness”重新布局。   復制代碼 struct BadLayout {         auto user0_data0;         auto user1_data1;         auto user0_data1;         auto user1_data0; };   struct GoodLayout {     auto user0_data0;     auto user0_data1;     auto user1_data0;     auto user1_data1;   }; 復制代碼         最後,可維護性!這一點非常重要,對於一些生命周期較長的項目來說,把數據按邏輯組織更易於維護,減少潛在bug的重要性,如果性能差別不大,我通常更願意讓代碼看起來好讀J       2.  Code cache   復制代碼 struct BitBool {          bool b0 :1;          bool b1 :1;          bool b2 :1; }   struct NormalBool {          bool b0;          bool b1;          bool b2;    } 復制代碼       上次的例子中,這段代碼比較有爭議,讓我們從時間和空間方面來分析。時間上,因為BitBool需要額外指令來訪問元素,因此效率一定比NormalBool低,但差別非常小,幾乎可以忽略。再看空間上,但從結構本身看,顯然BitBool更小,但是由於訪問元素需要額外指令,實際應用中,生成的代碼一定比NormalBool多,讀取訪問的次數越多,生成的代碼也越多(內聯的結果),而代碼也需要占用內存空間!!cache line中通常一半是代碼,一半是數據。因此,不一定因為BitBool本身小就得到更好的cache。大部分文章在討論cache missing時都只介紹了數據,而忽略了代碼也需要占用內存,也會有cache missing。某些游戲引擎會在update entity時先把對象按照類型排序,就是為了減少代碼的cache missing。            最後,這個例子的目的是讓大家了解過度優化可能並不會帶來性能提升,實際應用中兩種寫法的雖然有性能差距,但基本可以忽略。    3.  more about bit field        上一個例子讓我想起了bitfield另一個微妙的地方,假設f1和f2在兩個不同線程中,考慮下面代碼是安全的嗎?   復制代碼 struct BitField {          bool b0 : 1;          bool b1 : 1;          bool b2 : 1;          uint8_t i0 :3; }   BitField bf; std::mutex mtx1; std::mutex mtx2; //thread 1 void f1() {       mtx1.lock();       bf.b1 = somevalue;       mtx1.unlock(); } //thread 2 void f2() {     mtx2.lock();     bf.i0 = somevalue;     mtx2.unlock(); } 復制代碼                  No!!!雖然代碼可以通過編譯運行,但卻並不是線程安全的,因為b1,i0都屬於同一快內存”單元”,因此根本無法生成只更新b1,但是不寫入i0的代碼!!實際上c++11明確指出了這種情況會導致race,臨近的bit總是被當做一個”對象” :)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved