2012-05-28 55 views
4

使用指針時會有性能影響嗎?指針和性能影響?

是不是更好避免使用指針,如果是這樣,在什麼情況下?顯然,他們和參考文獻一起幫助減少數據複製。我認爲如果指向的數據類型很小,指針的需求就會變小。相反,最好通過指針傳遞一個大對象,因爲指針的開銷小於複製對象的開銷。

我也想知道除參數/參數以外的區域中的指針嗎?

引用通常比這個性能上下文中的指針更好嗎?

我讚賞我接近微優化的SO「骯髒」主題,但我正在編寫一個非常專注於延遲的應用程序。

+1

對於它的價值,引用的方式完全相同的指針通常實現的,所以不會有性能差異。 –

+0

局部性可能會影響性能,但我不想對此做任何猜測。我的意思是你應該衡量一下。 –

+0

根據您的程序邏輯選擇使用引用,指針和值,而不是對性能的期望。然後分析你的代碼,如果你發現結果有趣,請告訴我們。 –

回答

6

我知道性能可能很重要,但是語義是更多重要的是:快和錯是無用的。

使用指針或引用具有語義含義,比如分享:

void foo(A& a) { 
    a.a = 1; 
    if (a.b != 0) { throw ... } 
    a.b = 0; 
} 

在這種情況下a.b == 0,那麼a第一場已經變更,但是不使用其第二。

而且,這種共享可以產生潛在的混疊:

void foo(struct A a, struct A b); 

void foo(struct A* a, struct A* b); 

在第一種情況中,兩個結構是必然不同,但在後者它們不是。這種可能的別名可能會阻止優化。

首先關注語義。一旦你得到了他們的權利,你可以調整和衡量在你的特定情況下的影響。

+1

用於'語義'的+1。 –

+0

當downvote沒有伴隨理由時,我只是討厭它。我明白,一個特別糟糕的答案可能會很快被低估,它有助於保持乾淨......但總的來說,沒有解釋**爲什麼**沒有什麼意義。 –

1

通過指針訪問數據比直接訪問數據要慢一些,但解引用操作非常快,除非您正在執行一些非常具體的重複性數字運算任務,否則這通常不是什麼大事。

對於傳遞大對象或小對象的指針,你完全正確。例如,int *和int的大小可能相同,具體取決於實現。

引用和指針通常與性能相同。但是,如果您習慣於使用const Foo &而不是Foo *,那麼編譯器通常可以更好地優化代碼。

+0

你可以備份你的第一句話嗎? –

+0

當然 - 通過指針訪問數據需要2個內存讀取:1指針找出從哪裏讀取數據。今天它不是什麼大問題,但是它已經是30年前了。 – gbjbaanb

+0

我的意思是支持一個可靠的來源。我懷疑是否有任何收益。 –

4

引用通常比這個性能上下文中的指針更好嗎?

是的,當你可以的時候使用引用,當你必須的時候使用引用。 從表現上看,它們是一樣的。

通過引用或指針傳遞大型結構通常會更好,以防止額外的複製,是的。

通過指針或引用訪問變量或對象可能是 ,與直接訪問變量或對象一樣快。這種效率的原因在於微處理器的構造方式。所有在函數中聲明的非靜態變量和對象都存儲在 堆棧中,實際上是相對於堆棧指針進行尋址的。 同樣,類 中聲明的所有非靜態變量和對象都可以通過C++中已知的隱式指針訪問爲「this」。因此,我們可以得出結論:在一個結構良好的C++ 程序中,大多數變量事實上都是通過指針以某種方式訪問​​的。因此,必須設計微處理器以便使指針 有效,這就是它們。

一些缺點,雖然,但他們同時適用於指針和引用:

不過,也有使用指針和引用的缺點。 最重要的是,它需要一個額外的寄存器來保存指針或引用的值 。寄存器是一種稀缺資源,尤其是在32位模式下的 。如果沒有足夠的寄存器,則每次使用指針 都必須從內存中加載,這會使程序變慢。另一個缺點是 指針的值在變量 指向的時間可以被訪問之前的幾個時鐘週期內被需要。

here is the source。如果你問這個問題,我想你會發現它是一個很好的閱讀。

讓我們看看一些代碼:

int x = 0; 
00412E0E mov   dword ptr [x],0 
    foo(x); 
00412E15 lea   eax,[x] 
00412E18 push  eax 
00412E19 call  foo (4111C2h) 
00412E1E add   esp,4 
    foo(&x); 
00412E21 lea   eax,[x] 
00412E24 push  eax 
00412E25 call  foo (4111BDh) 
00412E2A add   esp,4 

沒有區別調用函數時。

void foo (int& x) 
{ 
00411370 push  ebp 
00411371 mov   ebp,esp 
00411373 sub   esp,0C0h 
00411379 push  ebx 
0041137A push  esi 
0041137B push  edi 
0041137C lea   edi,[ebp-0C0h] 
00411382 mov   ecx,30h 
00411387 mov   eax,0CCCCCCCCh 
0041138C rep stos dword ptr es:[edi] 
    x = 3; 
0041138E mov   eax,dword ptr [x] 
00411391 mov   dword ptr [eax],3 
} 

void foo (int* x) 
{ 
004117A0 push  ebp 
004117A1 mov   ebp,esp 
004117A3 sub   esp,0C0h 
004117A9 push  ebx 
004117AA push  esi 
004117AB push  edi 
004117AC lea   edi,[ebp-0C0h] 
004117B2 mov   ecx,30h 
004117B7 mov   eax,0CCCCCCCCh 
004117BC rep stos dword ptr es:[edi] 
    *x = 3; 
004117BE mov   eax,dword ptr [x] 
004117C1 mov   dword ptr [eax],3 
} 

功能裏面沒有什麼區別。

+1

是的,沒有。指針算術是一個巨大的性能增益。 –

+0

@Cicada如果你想做指針算術,你必然會使用指針。 –

-2

使用指針沒有性能影響。指針的使用非常快。指針被用作大多數(所有?)專業項目中的常用工具。它們用於建立列表,隊列,地圖,樹木,數據庫,每個地方。習慣他們!

+0

擁有大量指針需要非常多的間接尋址,這很慢(因爲你需要額外的指令,而且你可能會有大量的緩存未命中)。 [Bjarne Stroustrup](http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Keynote-Bjarne-Stroustrup-Cpp11-Style)在他的例子中解釋了這一點,爲什麼插入到結束列表通常比插入到矢量的末尾,正是出於這個原因。 – 2012-05-28 13:59:20

+6

我認爲即使使用更多驚歎號,您也不會錯。 –

1

基於堆的對象最大的問題是,當你需要它們時,它們通常不會被放置在一起,這意味着你需要更多的時間來讀取這些對象到CPU緩存中來處理它們(例如if你有10個需要處理的對象,如果它們全部被連續分配一個內存,那麼讀取會將它們全部移到緩存中,如果它們散佈在整個堆中,則需要爲它們中的每一個讀取) 。公平地說,這適用於所有堆系統,包括垃圾收集後的垃圾收集系統。答案(顯然)是將這些對象分配在一起,或者爲緩衝區提供可用空間以隨時分配它們。

指針的另一個問題是你需要間接訪問指針對象。如果你有一個對象數組,你很好 - 它們通常是連續的。但是,如果你有一個指針數組,你必須閱讀這些指針才能找出讀取對象的位置。現在,指針也可以很快 - 如果你使用指針運算來訪問你的連續對象(因爲你只需要一個指針來讀取所有對象的位置)。

因此,簡而言之,問題不在於指針本身,而在於如何組織它們指向的數據。

+2

請注意,您也可以使用指向堆棧中對象的指針,以避免複製大型結構。 –

1

這取決於指向的指針在哪裏。如果在堆棧上分配的對象,並且在許多情況下傳遞指針參數的對象將比指向堆上的對象的指針快。如果你傳遞的對象在被CPU緩存的大部分機會之前被主動使用,那麼性能將大致相同。

關於複製...大多數編譯器將使用CPU寄存器來傳遞指針,這是CPU中速度最快的內存。但是,如果您將通過值編譯器將需要複製整個對象,並且還要調用ctor/dtor。

所以我的建議嘗試保持在堆棧上的東西,並通過指針/引用。

很難說性能不穩定,可能會在不同的硬件/操作系統/編譯器上發生變化,所以我的建議是使用profiler工具來分析代碼,以分析緩存未命中,內存碎片,CPU使用率等問題。

http://en.wikipedia.org/wiki/VTune

http://developer.amd.com/tools/CodeAnalyst/Pages/default.aspx