執行人: kevinzxc1217
專題解說錄影
popo8712
在你的實驗中,有提到 LLFree 在多核系統中的效能提升主要來自於使用了 tree reservation 系統,避免了多核 CPU 之間的記憶體競爭。請問在這種情況下,如果多個 CPU 需要同一個 memory block,tree reservation 系統是如何有效地管理和調度記憶體資源,避免因為過多的 reservation 而導致的 memory fragmentation 問題的呢?
kevinzxc1217
透過 CPU-Local Tree Roots 方法,可以有效避免多個 CPU 競爭相同記憶體位址的問題,主要操作是當某個 CPU 決定要 reserve tree 時,它會將該 tree 的 cU 移動到 Local CPU 的 cP 中,同時將對應 Globel Counter 的 cU 設置為 0。
LindaTing0106
提到 LLFree 其中之一的優點為抗碎片化,想請問原因是?
kevinzxc1217
LLfree 配置和釋放時,只會針對固定大小的 Frame(4KB)進行管理,避免產生大小不一的記憶體區塊,減少碎片化的發生。
hugo0406
Preferred Tree
在初始化時,每個 CPU 會選擇一個 Preferred tree,當 CPU 需要配置記憶體空間時,會在自己的 Preferred tree 中進行搜尋和配置,避免多個 CPU 之間的競爭,提升配置效率。
Preferred Tree 是為了避免初期 CPU 競爭而配置的初始化記憶體區塊,想問是以什麼樣的方式去做不衝突的分派呢?是採取完全隨機配置嗎?若是使用在 hash 發生衝突的解決方式是否可以有效避免碰撞產生?
kevinzxc1217
初起 CPU 做配置時,會依照 Allocation Strategies 方式去進行 tree 的分配,後續會在依照 Reserve-on-Free 策略去分配 CPU 的 Preferred Tree,即其中一個 CPU 對同一 tree 連續進行 4 次以上的釋放時,將把該 tree 標記為該 CPU 的 Preferred tree。
yinghuaxia
Last Tree
用於記錄該 CPU 最近一次釋放記憶體空間的 tree,助於優化記憶體重新配置的效率。當 Preferred tree 空間不足時,會優先查找 Last tree。
此段落中提到 Last Tree 會負責記錄上一次 CPU 剛釋放的記憶體空間,若發生同時多個 CPU 的 Last Tree 記錄到同一記憶體位置的情形,此時產生的衝突該如何解決?
kevinzxc1217
每個 CPU 會透過 atomic 的操作去 reserve tree,若 CPU1 先拿走被共同記錄的 Last tree,則 CPU2 需去尋找額外的 free tree 或 partial tree 來做使用。
st10740
被配置的 frame 大小有分為 4 KB、2 MB 和 1 GB,請問選擇這些大小的原因是什麼呢?
kevinzxc1217
記憶體管理單位(MMU)通常是根據特定標準設計的,這些標準在不同的硬體平台之間保持一致性,以便系統能夠跨平台運行,這裡 Frame 大小的設置採用是採用 AMD64 MMU 的框架大小(4 KiB、2 MiB、1 GiB)進行討論。
探討高效的記憶體配置器如何設計和實作。
理解高效的記憶體配置器如何設計和實作。
補強對應的背景知識,回顧課程教材。
如何解決多核處理器中,allocator 會面臨大量的 lock contention 呢?
若要有更好的 scalability,就需要 lock-free 的實作
Lock-free
以系統的觀點來看,只要執行足夠長的時間,至少會有一個執行緒會有進展 (progress)。在 LLFree 配置器中,lock-free 技術被用來實現頁框配置和釋放的操作,確保在並行環境下能夠高效地管理記憶體。
Log-free
在執行某些操作時,不使用傳統的 log 來記錄狀態變更,可以減少 log 記錄和處理的開銷,透過 Atomic 操作來取代 log 記錄,可以提高系統性能。
不要濫用「具體」ㄧ詞,查閱教育部重編國語詞典「具體」的釋義:
本處就算刪去「具體」,只說「目標包括」也文意通順。使用「具體」該有明確的理由,特別是對比「籠統」和「不完備」的語境。改進你的漢語表達,才能讓溝通更有效率。
已改進,謝謝提點
LLFree 是一種新型記憶體配置器,使用 Rust 語言撰寫,目的是提高記憶體管理的安全性和效能,目標包括:
注意用語:
務必詳閱 https://hackmd.io/@sysprog/it-vocabulary 和下方的詞彙對照表
已改進
LLFree vs Linux frame allocator
提供更完整的解釋,特別是同類型詞彙和選擇之間的比較。
已更新
解釋 frame 在通用的記憶體配置器和論文中的作用,特別是分割和分類的策略差異。
已完成
Lower Level - Allocation Mechanisms
Upper Level - Allocation Strategies
CPU-Local Tree Roots:
具有多個 CPU 的系統,當某個 CPU 決定要 reserve tree 時,它會將該 tree 的 cU 移動到 Local CPU 的 cP 中,同時將對應 Globel Counter 的 cU 設置為 0。之後該 CPU 的配置和釋放操作只會影響 Local Counter 的 cP,而其他 CPU 則無法對此 Tree 再進行配置。但是,其他 CPU 先前有保留該 tree 部分 Frame 的話,仍可以對該 tree 進行釋放,這時只會增加 Globel Counter cU,而不會影響 CPU 記錄的 cP。這樣可以確保在多個 CPU 同時操作 tree 時,不會產生 False sharing 的問題。
Reserve-on-Free:
具有多個 CPU 的系統中,當其中一個 CPU1 對同一 tree 連續進行 4 次以上的釋放時,
將把該 tree 標記為 CPU1 的 Preferred tree。這意味著即使後來將該 tree 被配置給了另一個 CPU2 ,或者該釋放操作是由其他 CPU3 觸發的,釋放時然優先返回到 CPU1 的 Preferred tree 中。
整體架構分成 Upper Level 和 Lower Level ,其中 Uppper Level 包含 Tree ,而 Lower Level 包含 Children 和 Bit Fields。
在介紹個別欄位和程式碼之前,你該論述何以 LLFree 能解決其宣稱的問題。也就是該先介紹關鍵操作、突破手法,然後才深究細節。
好的,已調整內文順序
使用 Bitfield 的好處是:
注意用語:
務必使用本課程教材規範的術語。
說明此處使用 bit field 的必要。
已補充說明
tree
rows
有 64 bits ,總共有 512 個 bits 可以記錄 512 個 frame ,當 frame 被配置時,對應的位元被設定為 1,未被配置時,對應的位元被設定為 0。LLFREE_CACHE_SIZE
的大小對齊。這樣做可以提高記憶體存取效率,特別是在多核處理器上,可以減少因 cache 不對齊而造成的效能損失。
這是剛開始的記憶體配置,64 MiB Entries 對應為 Upper Level 中的 Tree 結構,而 2 MiB Entries 對應為 Lower Level 中的 Child 結構, 512 bit 對應為 Bitfeild 結構。其中 Free Counter 記錄該結構下所剩餘的 Free Frame 數量,Bit Flag 用來追蹤哪些區塊可用於合併,1 代表可以,0 代表不行。
CPU 0 reserve 了有著 7 個 Frame 的 Tree,而 CPU 1 reserve 了有著 5 個 Frame 的 Tree,將原先 Tree 上的 Global counter 歸 0,將值自行記錄在各 CPU 的 Local counter 上,使得其他 CPU 要在借用時會因為看到 Global counter 為 0 而無法使用。
當 CPU 0 使用了 reserve 的空間,第一步會將 local counter 從 7 變成 6 ;第二步會修正 2 MiB Entries(Child)內的值,優先使用已 allocated 的 frame ,從 3 變成 2 ;第三步把對應的 512 bit(Bitfiled) 內的 0 轉成 1 。
補上實驗硬體和相關實驗流程,讓其他人也可重現。
已補充說明
本實驗主要參考 llfree-bench 的效能評估程式
這裡主要是測試 LLFree 和 Linux Buddy allocator 的性能,在 Bulk 和 Rand 測試中,LLFree 相較於 Linux buddy allocator 速度明顯快許多,在 Repeat 測試中,即相同 frame 被反覆重新配置的情況下,由於 Linux 將 Cache 擴展到 order 1-3 和 order 9,使得 Linux buddy allocator 在特定大小的記憶體配置釋放上比 LLFree 速度更快。
這裡是測試 LLFree 在不同核數量上的速度比較,在 Bulk 和 Repeat 的 userspace 基準測試中,我們可以觀察到 LLFree 的操作時間幾乎保持不變,不受核數量影響,是因為 LLFree 的配置和釋放有了 reservation 系統,而減少了大多數的共享所需花費的時間。
只有在 Rand 測試時,由於 Cache 失效和 child counters 的更新頻繁,我們才會看到越多核的影響速度越慢。但這裡即使有 8 個並行請求 frame 的核, 最多時間花費也不到 170 ns。
這裡是測試 LLFree 不同 Order 數量時的速度比較,在 Bulk 和 Repeat 測試時,每個操作的時間都少於 100 ns;而 Random 操作成本較高,因為會發生 cache-miss 和 cache invalidations 而最高需要 190 ns。
這裡不清楚為什麼 Order8 相較於 Order9 和 Order10 花費更多的時間?
這個實驗是你做的嗎?如何解讀?
倘若你只是作論文的「懶人包」,卻沒有重現實驗、指出裡頭的錯誤和不足,那我們直接用 AI 工具來摘錄就好,不是嗎?人就該做到 AI 做不到的事!
好的沒問題,論文上該實驗共比較 26 核,這裡是我重現實驗共使用了 9 核,目前也在嘗試使用其他方法呈現效能評估中。
這裡測試了三種 List-based 配置器和 LLFree 配置器做比較,並評估其性能:
注意用語!
已改進
這裡我使用了 9 核(可自訂)進行比較,在 Bulk 測試中,隨著更多核的測試,LLFree 花費時間皆和論文呈現相同的平緩成長,而 ListCAS 呈現明顯的向上趨勢。論文使用 26 核進行實驗,在 20 核以後 ListCAS 更是明顯的超過 LLFree 所花費的時間。
對比論文和你重現的實驗,解說其中異同處。
已補充說明
分類解釋 (有可能會遇到編譯/執行的錯誤,善用 GitHub 提交 issue / pull request)
llfree-c/tests 該測試程式主要是測試 llfree-c/src 內的所有函式功能是否正確,使用declare_test
去進行測試,每個函式測試內部都分成actual
和expect
,來判斷actual
經過指定函式後,產生出來的值是否和expect
相同。
若測試程式成功運行,會如上呈現。
注意書寫規範:
已更正
這裡主要是測試 field_set_next
是否正常運作,field_set_next
該函式功能為設置 bitfield 中第一個為 0 的 bit 轉為 1,用來配置原本空閒的 frame 做使用,可以看到測試程式原先 bf(bitfield)記錄的是 0x1…,經過 field_set_next
函式後將原本 0x1 的下個 0 改為 1 ,則變成 0x3…,由此可見函式的正確性。
注意書寫規範:
已更正
field_toggle
將位元欄位中指定位置的位元值進行翻轉,其中 &actual
為指向 bitfield 的指標; pos
為該 bitfield 上的指定位置; 0
這裡的數字表示 ,看需要連續多少位元進行翻轉;true
這裡表示要配置或釋放 frame , true 為配置指定 frame ,即位元從 1 變 0, 而 false 為釋出指定 frame ,即位元從 0 變 1。由此測試程式可以看到 actual
內的 bf 其 0x1 經過 field_toggle
函式後有成功翻轉成 0x0。
注意書寫規範:
已更正
field_count_ones
用於計算 bitfield 中值為 1 的位元數量,第一次測試 actual
經過函式應該要回傳 0,透過 check_equal_m
來判斷 ret
是否為 0;第二次測試 actual
經過函式應該要回傳 2 ,同樣也檢查 ret
是否為 2。主要測試了計算結果是否正確以及位元經過函式後是否有被修改。
注意書寫規範:
已更正
這段程式主要是去測試 child_inc
該函式是否正確執行,第一次建立大小為 0x200 的 actual
,由於是 huge
型態,所以無法新增空閒 frame 。第二次建立大小為 5 的 actual ,由於沒有超出最大限制且非 huge
型態,所以經過 child_inc
函式後有成功新增一個 frame ,變成 6 。
這裡類似前述 child_counter_inc
函式的操作,不過是相反的,這裡是減少指定 frame 數量,可以看到原本建立的 actual
為 0 個 frame,所以經過 child_dec
函式後會回傳 false ,因 0 本身是最小值了,無法再減少。
注意用語:
二者的語境不同。
已更正
這裡是測試 llfree_init
函式是否有成功初始化,llfree_new
建立一個 1 GiB 的 frame 和 4 核 CPU,透過 llfree_frames
計算建立的 upper 內是否為 1 MiB,以及透過 llfree_free_frames
計算空閒的 frame 是否為 1 MiB,最後再使用 llfree_validate
函式進行驗證整體是否正確。
其中值得注意的是當 llfree_new
最後傳入的參數為 LLFREE_INIT_FREE
表示建立 frame 皆為空閒的,若是 LLFREE_INIT_ALLOC
則表示建立的 frame 皆為被使用的。
首先 llfree_new
建立一個 1 GiB 的 frame 和 4 核 CPU,由於建立時是使用 LLFREE_INIT_ALLOC
當作參數,表示會將所有建立的 frame 當作已被配置。再透過 llfree_frames
以及 llfree_free_frames
來檢查是否有正確的 frames 和 free_frames 數量, 並且分別測試大型記憶體區塊和小型記憶體區塊使用 llfree_put
和 llfree_get
函式時功能是否正常,llfree_put
用於將 frame 釋放為空閒,而 llfree_get
用於配置空閒中的 frame。
建立一個 lower
型態的 actual
,再透過 lower_get
函式去配置一個空閒的 frame 給它,若該函式運行正確,其返回的 ret
應會將指定位址的 frame 拿去使用,即對應位元的 0 變成 1。這裡一開始建立的是全部空閒的 frame,經過 lower_get
後會將首個位元設置為 1,透過heck_equal_bitfield
函式比較後可以發現結果如預期。
建立一個 counter 為 764 的 tree,經過 tree_reserve
後,由於該 tree 上的 frame 都被保留了,所以 counter 會變為 0,且回傳 true。搭配 assert,可以從程式執行過程中判斷是否符合預期,若有錯誤,則產生錯誤資訊。
依據論文 LLFree: Scalable and Optionally-Persistent Page-Frame Allocation 的陳述,嘗試比較上述實作和其他記憶體配置器的表現。
這裡比較 LLFree 和 TLSF 的實作,並說明 TLSF 的做法是什麼,也可以參考去年 Linux 核心專題: TLSF 實作 有詳細的介紹。
TLSF(Two-Level Segregated Fit)是一個高效的動態記憶體配置器,專為即時系統設計。它的主要目標是提供時間複雜度(O(1))的配置和釋放操作,同時將記憶體碎片最小化。
LLFree 和 TLSF 都是高效的記憶體配置器,它們都透過分級管理來實現快速配置和釋放操作,但在資料結構、應對策略上有著不同的方法,選擇哪種記憶體配置器,取決於具體應用和系統需求,也要考量其限制。
上述總結過於武斷,且沒有闡述 TLSF 的限制,特別是其實作手法。
LLFree 也有限制,例如其初始化的成本,應予以探討。
已更正