2015-06-20 112 views
0

常數變量我有一個vector使用.size()VS for循環

vector<Body*> Bodies; 

而且它包含指向我已經定義了Body對象。

我還有一個unsigned int const,其中包含bodies中我希望擁有的body個對象的數量。

unsigned int const NumParticles = 1000; 

我已填充BodiesNumParticlesBody對象。

現在,如果我想通過一個循環迭代,比如調用每個Body的更新()函數Bodies,我有什麼我可以做兩種選擇:

第一:

for (unsigned int i = 0; i < NumParticles; i++) 
{ 
    Bodies.at(i)->Update(); 
} 

或第二:

for (unsigned int i = 0; i < Bodies.size(); i++) 
{ 
    Bodies.at(i)->Update(); 
} 

有親的和每一個反對的。在安全性,可讀性和慣例方面,我想知道哪一個(如果有的話)是更好的做法。

+2

_ _'Bodies.size( )'當然。 –

回答

1

我建議你使用.size()函數,而不是定義一個新的常量。

爲什麼?

  1. 安全性:由於.size()不拋出任何異常,這是完全可以放心使用.size()

  2. 可讀性:恕我直言,Bodies.size()傳達Bodies的大小比NumParticles更清楚。

  3. 約定:根據約定,最好使用.size(),因爲它是矢量的屬性,而不是變量NumParticles

  4. 性能:.size()不斷複雜成員函數,所以使用const int.size()之間沒有顯著的性能差異。

2

我會投票支持的範圍

for (auto* body : Bodies) 
{ 
    body->Update(); 
} 
+0

你的意思是寫'body-> Update();'Body-> Update();''insted? –

+0

@KierenPearson:case typo fixed,thanks – Jarod42

3

我希望,因爲編譯器(至少在這種情況下)可以內聯的所有相關代碼在std::vector,這將是相同的代碼[除了1000是機器碼中真正的常量字面量,並且Bodies.size()將是一個「可變」值]。

發現的簡短摘要:

  • 編譯器不要求每次迭代的載體size()功能,它計算的是,在循環的開始,並使用它作爲一個「常數值」。

  • 循環中的實際代碼是相同的,只有循環的準備是不同的。

  • 一如既往:如果性能非常重要,請使用數據和編譯器測量系統。否則,寫最有意義爲您設計的代碼(我更喜歡使用for(auto i : vec),因爲這是簡單,直接的[並適用於所有的容器])

佐證:

取咖啡後,我寫了這個代碼:

class X 
{ 
public: 
    void Update() { x++; } 
    operator int() { return x; } 
private: 
    int x = rand(); 
}; 

extern std::vector<X*> vec; 
const size_t vec_size = 1000; 

void Process1() 
{ 
    for(auto i : vec) 
    { 
     i->Update(); 
    } 
} 

void Process2() 
{ 
    for(size_t i = 0; i < vec.size(); i++) 
    { 
     vec[i]->Update(); 
    } 
} 


void Process3() 
{ 
    for(size_t i = 0; i < vec_size; i++) 
    { 
     vec[i]->Update(); 
    } 
} 

(帶main功能,填補了陣列一起,並調用處理1(),進程2()和Process3() - 該main是在一個單獨的文件,以避免編譯器決定內聯的一切,使得它很難說什麼是什麼)

這裏是由g ++ 4.9.2生成的代碼:

0000000000401940 <_Z8Process1v>: 
    401940: 48 8b 0d a1 18 20 00 mov 0x2018a1(%rip),%rcx  # 6031e8 <vec+0x8> 
    401947: 48 8b 05 92 18 20 00 mov 0x201892(%rip),%rax  # 6031e0 <vec> 
    40194e: 48 39 c1    cmp %rax,%rcx 
    401951: 74 14     je  401967 <_Z8Process1v+0x27> 
    401953: 0f 1f 44 00 00   nopl 0x0(%rax,%rax,1) 
    401958: 48 8b 10    mov (%rax),%rdx 
    40195b: 48 83 c0 08    add $0x8,%rax 
    40195f: 83 02 01    addl $0x1,(%rdx) 
    401962: 48 39 c1    cmp %rax,%rcx 
    401965: 75 f1     jne 401958 <_Z8Process1v+0x18> 
    401967: f3 c3     repz retq 

0000000000401970 <_Z8Process2v>: 
    401970: 48 8b 35 69 18 20 00 mov 0x201869(%rip),%rsi  # 6031e0 <vec> 
    401977: 48 8b 0d 6a 18 20 00 mov 0x20186a(%rip),%rcx  # 6031e8 <vec+0x8> 
    40197e: 31 c0     xor %eax,%eax 
    401980: 48 29 f1    sub %rsi,%rcx 
    401983: 48 c1 f9 03    sar $0x3,%rcx 
    401987: 48 85 c9    test %rcx,%rcx 
    40198a: 74 14     je  4019a0 <_Z8Process2v+0x30> 
    40198c: 0f 1f 40 00    nopl 0x0(%rax) 
    401990: 48 8b 14 c6    mov (%rsi,%rax,8),%rdx 
    401994: 48 83 c0 01    add $0x1,%rax 
    401998: 83 02 01    addl $0x1,(%rdx) 
    40199b: 48 39 c8    cmp %rcx,%rax 
    40199e: 75 f0     jne 401990 <_Z8Process2v+0x20> 
    4019a0: f3 c3     repz retq 

00000000004019b0 <_Z8Process3v>: 
    4019b0: 48 8b 05 29 18 20 00 mov 0x201829(%rip),%rax  # 6031e0 <vec> 
    4019b7: 48 8d 88 40 1f 00 00 lea 0x1f40(%rax),%rcx 
    4019be: 66 90     xchg %ax,%ax 
    4019c0: 48 8b 10    mov (%rax),%rdx 
    4019c3: 48 83 c0 08    add $0x8,%rax 
    4019c7: 83 02 01    addl $0x1,(%rdx) 
    4019ca: 48 39 c8    cmp %rcx,%rax 
    4019cd: 75 f1     jne 4019c0 <_Z8Process3v+0x10> 
    4019cf: f3 c3     repz retq 

雖然彙編代碼如下對於這些情況中的每一個都略有不同,實際上,我會說你會很難推測這些循環之間的差異,事實上,在代碼上運行perf表明它是「所有循環的同一時間「[這是100000個元素和100個調用Process1,Process2和Process3的一個循環,否則時間主要爲main中的new X]:

31.29% a.out a.out    [.] Process1 
    31.28% a.out a.out    [.] Process3 
    31.13% a.out a.out    [.] Process2 

除非你認爲1/10的百分比是顯著 - 這可能是東西,需要一個星期來運行,但是這只是一秒鐘[我的機器上0.163秒]零點幾,和可能比其他任何測量誤差更大 - 實際上理論上最短的時間應該是最慢的,Process2,使用vec.size()。我以更高的循環數進行了另一次運行,現在每個循環的測量值相互之間爲0.01% - 換言之,花費的時間相同。當然,如果你仔細觀察,你會發現除了Process3的早期部分外,所有三種變體的實際循環內容基本相同,因爲編譯器知道我們將至少執行一個循環 - Process1Process2必須在第一次迭代之前檢查「是空載體」。這對於很短的向量長度會有所不同。

+0

哇。其實哇我呼吸了。當我提出這個問題時,我期望對「不好」問題有否定迴應,或者根本沒有迴應。你在這個答案中的這項工作是驚人的。你應該得到你所有的代表 –

2

NumParticles不是矢量圖的屬性。這是相對於矢量的一些外部常量。我寧願使用矢量的屬性size()。在這種情況下,讀者的代碼更安全,更清晰。

通常使用一些常數而不是size()對於讀者來說意味着通常常數可以不等於size()

因此,如果你想告訴讀者你要處理矢量的所有元素,那麼最好使用size()。否則使用常量。

當重音放在常量上時,這個隱式規則當然也有例外。在這種情況下,最好使用常量。但這取決於上下文。

0

我更喜歡這種形式:「我想知道哪一個(如果任一)將是更好的做法,在安全性,可讀性和公約的規定」

for (auto const& it : Bodies) 
{ 
    it->Update(); 
}