C++ Low level performance optimize 2

  C++ Low level performance optimize 2 php

 

    上一篇 文章討論了一些底層代碼的優化技巧,本文繼續討論一些相關的內容。html

     首先,上一篇文章討論cache missing的重要性時,用了list作比較,目的並非說list沒有用,而是說明cache missing會對性能有重要影響。若是元素很少,而且對象複製的代價很大,那麼list可能就是更好的選擇。其次,這裏討論的大部分是編碼時一些比較底層的技巧,當遇到性能問題時,應該先考慮是否能在高層改進算法,減小運算,實在不行,在考慮這類優化技巧。性能優化的挑戰就在於沒有完美的永遠適用的方案,瞭解這些技巧讓咱們在優化代碼時有更多武器,但最終選擇哪一個方案還須要更加實際狀況,而且以profile的實際數據爲依據來作。c++

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。ui

         最後,這個例子的目的是讓你們瞭解過分優化可能並不會帶來性能提高,實際應用中兩種寫法的雖然有性能差距,但基本能夠忽略。

 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老是被當作一個」對象」 :)

相關文章
相關標籤/搜索