2014-09-11 53 views
2

我正在讀'CLR通過C#',並且'在運行時如何聯繫'以及'原始,參考和值類型'我有點困惑。 如果我有下面的代碼從主叫 -原始類型不變性和堆棧?

void DoSomething(int x) 
{ 
    int m = x/2; 
    int n = SomeMethod1(m); 
     n = (n * 2) + x; 
    int k = SomeMethod2(n); 
     m += (k*3); 
} 

的代碼沒有任何用處,但我只是想了解當地的整數變量和內存分配的行爲。

我知道m,n和k位於堆棧上嗎? 現在,對於最後一行(我忽略變量n),必須修改'm'的值。因此,堆棧中的所有其他內容必須被彈出並且'm'的值被更新?相比堆上的裝箱和拆箱開銷,這可以忽略不計嗎?如果堆疊高度更高,它會變得重要嗎?

P.S:從討論中排除GC(對於裝箱和取消裝箱),這絕對是一種開銷,也只是試圖理解行爲,可能/可能不是實際情況。

+0

看起來你不明白堆棧變量尋址是如何工作的。它不是'pop' /'push',它只是從堆棧指針偏移,這是1-2 cpu指令(糾正我,如果我錯了)。所以與拳擊/拆箱相比,沒有任何開銷。 – Sinatr 2014-09-11 09:23:54

+0

@Sinatr:它檢查/比較堆棧中的每個變量,直到它變爲'm'並更新值? – Kodathon 2014-09-11 09:36:16

+0

不是,'m'被引用爲'ptr + 123'(舉例),其中'ptr'是堆棧指針(寄存器?),而'123'是一個偏移量。因此,任何使用'm'的操作都不取決於它在堆棧中的深度。另外,也許你認爲,什麼'ptr'是*當前堆棧指針*,在'n'和'k'之後改變?這是不對的,'ptr'對一個進程(線程?)是恆定的,它是*堆棧的開始*(或者輸入函數時的堆棧點,直到函數退出時它們保持不變)。搜索'stack'解釋,也許[this](http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/stack.html)可以。 – Sinatr 2014-09-11 10:40:58

回答

1

不,沒有什麼說m,n或k應該在棧上。最有可能的是,他們將會登記註冊,而且因爲你甚至沒有使用相互依賴關係,他們甚至不必同時在那裏。 JIT編譯器在運行時優化方面非常出色,除了方法參數(或其引用)之外,堆棧幾乎不用於任何其他任何東西。 GC在寄存器或堆棧中都沒有權力 - 堆棧以與本地代碼相同的方式釋放。只有堆是GC的一個域。

這是一個常見的誤解,因爲在IL(中間語言C#編譯成)實際上確實是所有使用堆棧。但是,這是而不是什麼是在您的機器上執行 - IL代碼再次由JIT編譯器編譯。當然,JIT編譯器也可以自由地處理堆棧中的所有內容,但這很愚蠢。簡單例子時間:

x + y 

將彙編本IL(僞代碼):

push y 
push x 
op_Add 

因此,對於一個簡單的整數加法,它需要做的方法調用和兩個推入堆棧。不是非常昂貴,但如果你想要做任何嚴肅的計算,你就有麻煩了。

然而,JIT編譯器會更喜歡這樣生產的東西:

add ax, bx 

這是關於這個非常聰明,所以它實際上是經常使用的寄存器甚至方法參數 - 如果它是安全的。

但是,請注意,所有這些只是一個實現細節 - 在這種情況下,性能優化。一個整數可能很容易在堆上生存(事實上,如果它是例如盒裝的或者堆中存在的另一個對象的一部分)。

所以,顯然,最快的事情是那些在CPU本身內部支持的東西 - 就像上面的例子一樣,將兩個寄存器加在一起。非常快。

使用堆棧通常仍然非常快。這並不是說堆的分配是昂貴的 - 事實上,堆和堆棧分配的成本大致相同。這是釋放的痛苦 - 堆需要垃圾收集和壓縮,而堆只需要一個指針的變化。

哦,你誤解了你可以訪問堆棧的方式。確實,有基本的pushpop指令,但是,那些不是只有的方式。您可以像處理任何其他內存一樣直接處理堆棧的內存。事實上,這就是爲什麼你可以看到堆棧指針(如ESP)的全部觀點。

+0

如果整數被struct替換,這仍然適用? – Kodathon 2014-09-11 07:59:38

+0

@Kodathon它可能。或者它可能不會。這一切都取決於如何使用這些 - 例如,如果將結構傳遞給接口參數,結構必然會被裝箱,即使它實現了該接口。有幾件事你想避免,但總而言之,你甚至不應該在意。你所談論的一切都是性能優化,一旦它成爲一個明顯的問題,你應該只開始優化性能。更不用說,「優化」代碼非常容易,速度更慢,內存更密集等。測量是關鍵 - 配置文件,不要猜測。 – Luaan 2014-09-11 08:03:06

+0

當然,我同意何時開始優化部分。不知道我的問題實際上是否清晰 - 我只是想了解原始情況是否可行,並且修改堆棧CAN上的內容會產生影響? – Kodathon 2014-09-11 08:10:21

0

該代碼中的每個變量都是可變的並且處於堆棧狀態!

如果你想獲得一個什麼感覺不變性然後讀Eric Lippert張貼關於不變性

會有

m = m + (k*3); 
m += (k*3); 

區別是稍有差別在第二行中更好,因爲不會創建L1/L2緩存中的臨時註冊表元素(m +(k * 3)),並將值直接推回到變量值槽中。使其快3-12個週期(即1毫秒的1%以下)。儘量專注於重要的事情。

+0

我並不試圖找出m + =(k * 3)是否快於m = m +(k * 3)。我不是想優化我發佈的代碼(反正沒有做任何事情)。我正在讀這本書,只是想知道在某些時候堆棧有沒有任何開銷的可能性,因爲被修改的變量不在頂部。 – Kodathon 2014-09-11 09:19:04

+0

我的竅門是關於所有事情都存在開銷,但是你可以忽略它,因爲它太小了。 – Margus 2014-09-11 09:26:58

+0

足夠公平:)我應該增加'開銷與堆分配/取消分配相比' – Kodathon 2014-09-11 09:33:17