2013-10-18 58 views
15

似乎有一種觀點認爲在64位體系結構上使用「拆分堆棧」運行時模型是不必要的。我說的好象是,因爲我還沒有看到任何人居然說,只有圍繞它跳舞:拆分堆棧在amd64上不必要

一個典型的多線程程序的內存使用量可以減少顯著 ,因爲每個線程不需要最壞的情況下堆棧大小爲 。在32位地址空間中運行數百萬個線程(全NPTL 線程或協程)成爲可能。 - Ian Lance Taylor

...暗示64位地址空間已經可以處理它了。

而且......

...分離電池堆恆定的開銷和窄用例 (產卵的I/O密集型任務數量巨大的32位體系結構) 不接受... - bstrie

兩個問題:這是他們在說什麼?其次,如果是這樣,爲什麼他們在64位體系結構上沒有意義?

回答

19

是的,這就是他們所說的。

由於64位虛擬地址空間非常大,可以包含數百萬個堆棧地址範圍,如果需要,每個堆棧地址範圍都大到整個32位地址空間,因此在64位體系結構上(當前)不需要分割堆棧。

在使用Flat memory model如今,從虛擬地址到物理性的存儲位置的翻譯的支持下,hardware MMU的完成。在amd64事實證明,將64位虛擬地址空間的大塊保留到每個正在創建的新堆棧的同時,只將第一頁(4kB)映射到實際的RAM會更好(意思是總體上更快)。這樣,當操作系統重新配置MMU以將每個虛擬地址頁映射到實際的空閒地址時,通過連續的虛擬地址(意味着每個function prologue中的代碼較少,這是一個大的優化),堆棧將能夠根據需要增長和縮小RAM的頁面,每當堆棧增長或收縮高於/低於某些可配置的閾值時。通過巧妙地選擇閾值(例如參見dynamic arrays的理論),您可以在平均堆棧操作上實現O(1)複雜度,同時保留數百萬堆棧的好處,這些堆棧的增長可以根據需要進行消耗他們使用的內存。

PS:目前圍棋實施遠遠落後於任何這種:-)的

+0

理論最大堆棧大小是如何定義的? – thwd

+2

它沒有定義,它只是語言或編譯器的選擇。我應該更清楚,我會編輯它。 – Tobia

9

Go的核心團隊目前discussing在未來的Go版本中使用連續堆棧的可能性。

分離堆棧方法很有用,因爲堆棧可以更靈活地增長,但它也要求運行時分配相對較大的內存塊來分配這些堆棧。 Go的內存使用量有很多confusion,部分原因在於此。

製作連續但可增長(可重新定位)的堆棧可以提供相同的靈活性,並可能減少Go對內存使用的混淆。以及糾正低內存機器上的一些病態角落案例(請參閱鏈接線程)。關於32位和64位體系結構的優缺點,我認爲沒有任何直接關聯使用分段堆棧。

2

更新圍棋1.4(Q4 2014)

Change to the runtime

起來要走1.4,運行時(垃圾回收器,併發支持,接口管理,地圖,切片,字符串......)主要是用C編寫的,有一些彙編程序支持。
1.4,大部分代碼已被轉換爲Go,以便垃圾收集器可以掃描運行時的程序堆棧,並獲取有關哪些變量處於活動狀態的準確信息

該重寫允許1.4中的垃圾回收器完全精確,這意味着它知道程序中所有活動指針的位置。這意味着堆將會變小,因爲不會存在使非指針保持活躍的誤報。其他相關更改也會減小堆大小,相對於以前的版本,堆大小整體要小10%-30%。

結果是堆棧不再被分割,消除了「熱分裂」問題。當達到堆棧限制時,將分配一個新的更大的堆棧,並將所有活動的goroutine幀複製到此處,並且更新堆棧中的所有指針。


初始答案(2014年3月)

文章「Contiguous stacks in Go」由Agis Anastasopoulo也解決了這個問題

在這種情況下,堆棧邊界恰好落在緊密循環,重複創建和銷燬細分市場的開銷變得很大。
這被稱爲Go社區內的「熱分裂」問題。

「熱分裂」將通過實施連續堆棧在Go 1.3中解決。

現在,當堆需要成長,而不是分配一個新的細分市場將出現以下情況,:

  1. 創建一個新的,更大一些堆棧
  2. 複製舊堆棧的內容到新的堆棧
  3. 重新調整每一個複製指針指向的新地址
  4. 破壞舊堆棧

主要見於32位arhcitectures以下提到的一個問題:

有雖然有一定的挑戰。
1.2運行時不知道堆棧中的指針大小的單詞是否是實際指針。如果解釋爲指針,可能會有浮點數和大多數很少的整數實際上指向數據。

由於缺乏這些知識的垃圾收集器必須保守地考慮在堆棧幀的所有位置是根。這使得內存泄漏成爲可能,尤其是在32位架構上,因爲它們的地址池要小得多

但是,在複製堆棧時,必須避免這種情況,並且在重新調整時只應考慮真實指針。

Work was done thoughinformation about live stack pointers現在嵌入到二進制文件中,並且可用於運行時。
這不僅意味着1.3中的收集器可以使用precisely堆棧數據,但現在可以重新調整堆棧指針。