2015-03-08 67 views
2

我執行了一個小測試來確定訪問指針vs值向量向量的行爲。事實證明,對於小型內存塊而言,兩者的表現同樣出色,但是,對於大型內存塊而言,則會有顯着差異。向量指針vs向量值對大型內存塊和小型內存塊的性能損失

這種行爲的解釋是什麼?

對於下面的代碼,在我的電腦上執行,D = 0的差值約爲35%,而對於D = 10它是不明顯的。

int D = 0; 
int K = 1 << (22 - D); 
int J = 100 * (1 << D); 

int sum = 0; 
std::vector<int> a(K); 
std::iota(a.begin(), a.end(), 0); 
long start = clock(); 
for (int j = 0; j < J; ++j) 
    for (int i = 0; i < a.size(); ++i) 
     sum += a[i]; 
std::cout << double(clock() - start)/CLOCKS_PER_SEC << " " << sum << std::endl; 

sum = 0; 
std::vector<int*> b(a.size()); 
for (int i = 0; i < a.size(); ++i) b[i] = &a[i]; 
start = clock(); 
for (int j = 0; j < J; ++j) 
    for (int i = 0; i < b.size(); ++i) 
     sum += *b[i]; 
std::cout << double(clock() - start)/CLOCKS_PER_SEC << " " << sum << std::endl; 
+0

指針會導致間接尋址,從而導致額外的內存訪問。以及更多的空間正在使用當然可能會溢出緩存。 – 2015-03-08 19:04:21

+0

'sum + = a [i];'通過溢出有符號'int'的邊界導致未定義的行爲。 (並且另外發生相同的錯誤)。即使您認爲這不應該成爲問題,但使用不調用未定義行爲的代碼進行測試也是一個不錯的主意。這可以通過改變爲'unsigned int sum = 0;'(或其他一些無符號類型,儘管選擇的類型可能會影響基準)來解決。 – 2015-03-08 19:39:45

+0

如果您不處於最佳優化級別,基準並不意味着太多。如果使用「-O3」,則可能需要採取其他措施來防止優化循環。使'sum'變成'volatile'應該這樣做,但是最好仔細檢查生成的程序集以確保時序代碼不被重新排序。你也可以使用C++ 11的[高分辨率時鐘](http://stackoverflow.com/a/5524138/1505939)而不是'clock'。 – 2015-03-08 19:45:13

回答

1

從全局內存中獲取數據的速度很慢,所以CPU有一小段真正快速的內存來幫助內存訪問跟上處理器。處理內存請求時,您的計算機將通過請求所請求的位置周圍的所有請求並將它們存儲在緩存中,嘗試加速將來對內存中單個整數或指針的請求。一旦快速內存滿了,每當有新內容被請求時,必須擺脫其最不喜歡的位。

您的小問題可能完全適合或基本上適用於緩存,因此內存訪問速度非常快。大問題不能適應這個快速記憶,所以你有一個問題。矢量被存儲爲K個連續的存儲位置。當你訪問一個int的矢量時,它會加載int和他的一些附近值,這些值可以立即使用。但是,當您加載一個int*時,它會加載一個指向實際值的指針以及其他幾個指針。這佔用了一些記憶。然後,當您使用*取消引用時,它會加載實際值並可能附近有實際值。這佔用了更多的內存。您不僅需要執行更多的工作,而且還要更快地填充內存。實際增加的時間會有所不同,因爲它高度依賴於架構,操作(在這種情況下爲+)和內存速度。另外,你的編譯器會很努力地減少延遲。

相關問題