2012-06-15 56 views
4

有時,在查看優化代碼時,我發現僅在循環範圍內使用的參數將其聲明移至循環之外。在循環範圍外聲明參數會更有效嗎?

事情是這樣的:

A arr[BIG_NUMBER]; 

// .... // 

for (int i=0; i!=BIG_NUMBER; ++i) 
{ 
    B b = arr[i].getB(); 
    // ... do some work with b. 
} 

正在變成這樣:

A arr[BIG_NUMBER]; 

// .... // 

B b; 
for (int i=0; i!=BIG_NUMBER; ++i) 
{ 
    b = arr[i].getB(); 
    // ... do some work with b. 
} 

推測的理由是,我們節省了不斷重新聲明b。但這是一個合理的事情嗎?而且,答案根據B是原始類型還是類而不同?

我原以爲雖然for循環內變量的作用域限制可能會阻止它們在循環外被訪問,因爲循環的內容存在於同一個棧幀中,所以'actual'聲明只發生一次。

(NB,我已經考慮Creating an object in the loop但考慮到這是一個不同的問題,因爲它是與聲明,而不是初始化相關的任何費用。)

編輯 - 改進的標題

+1

@JoachimPileborg它的確很重要,如果構造/析構函數是昂貴的。 –

+0

編譯器也可以選擇調整塊進入/退出時的堆棧指針(不記得是否可以讓g ++來做到這一點,默認情況下它不會),雖然成本相對於循環構造。 –

+0

注意:額外的範圍可以通過將所有東西包裹在另一對大括號中來抵消。 '{B b; for(...){}}'是一個可接受的構造,通常用於RAII構造。 –

回答

8

如果它是一個原始類型,編譯器會相應地進行優化。

如果它是用戶定義的類,則取決於它。有什麼更昂貴的,一個額外的初始化或BIG_NUMBER析構?

比較這些:

B b; //one initialization 
for (int i=0; i!=BIG_NUMBER; ++i) 
{ 
    b = arr[i].getB(); //BIG_NUMBER assignments 
} 

for (int i=0; i!=BIG_NUMBER; ++i) 
{ 
    B b = arr[i].getB(); //BIG_NUMBER initializations 
          //should be the same as an assignment 
} //BIG_NUMBER objects destroyed 
+1

而當它產生變化的時候,當對象擁有一些昂貴的構造資源時,它不受賦值的影響(所以你應該在循環之外初始化)。或者,當'getB'按值返回時,你會得到一個複製elision,當然這對於賦值是不可能的(所以你應該在循環內初始化,或者交換而不是賦值)。移動語義主要減輕後者。 –

1

有沒有一般的答案。這取決於B的類型,您正在使用的編譯器 ,以及您可能在循環中(在 分配之後)做了什麼。你所能做的只是測量,甚至只會告訴你關於一個特定的編譯器,在一臺特定的機器上運行。