1 前置知識點學習(了解)
從CPU到實際的存儲節點,依據層級划分:Channel > DIMM > Rank > Chip > Bank > Row /Column
1.1 channel
CPU到內存的通路是channel,每個channel對應一個CPU的內存控制器,每個channel可以配有多個DIMM。
雙通道:CPU外核或北橋有兩個內存控制器,每個控制器控制一個內存通道。理論上內存帶寬增加一倍。
四通道同理。
1.2 DIMM
全稱Dual-Inline-Memory-Modules(雙列直插式存儲模塊),是目前最常見的內存模塊( 可以理解為內存條)。
以前的主機是直接將存儲芯片(chip)插在主板上的,然后發展出SIMM(Single In-line Memory Module),將多個chip焊在一片電路板上,成為內存模塊,再將它插到主板上。
1.3 Rank
DIMM上一部分或所有chip組成一個rank(64bit),因此內存至少需要有16片4bit的chip或者8bit的chip(不存在4bit和8bit芯片混搭的情況)。
內存控制器只允許CPU每次與內存進行一組64bits的數據交換,對應的就是一個rank。rank也可以理解為連接到同一個CS(chip select)的一組chip。
rank分類:
- Single-Rank(1R),要動用到DIMM上所有的chip,這些chip由同一個片選信號控制。
- Double-Rank(2R),產生2個64位rank,由2個片選信號控制,這2個片選信號是交錯的,不爭搶內存總線。
- Quad-Rank(4R),產生4個64位rank,由4個片選信號控制,這4個片選信號是交錯的,不爭搶內存總線。
在地址選擇時,只有當片選信號有效時,此片所連的地址線才有效。
1.4 chip
內存條上的黑色芯片就是chip,提供4bit/8bit/16bit/32bit的數據,提供4bit的芯片記作x4,提供8bit的芯片記作x8。
1.5 其他
再往下的bank、Row /Column這里可以暫時不用關心了,通過上文的示意圖中了解一下就行。
2 DPDK Mempool 庫
內存池是一個具有固定大小的對象分配器。 在DPDK中,它由名稱唯一標識,並且使用mempool handler來存儲空閑對象。 默認的mempool handler是基於ring的。它提供了一些可選的服務,例如“per-core緩存”和“內存對齊”,內存對齊能確保對象被填充,以在所有DRAM或DDR3通道上均勻分布。
這個庫由 Mbuf Library 使用。
2.1 Cookies保護字段
在調試模式中(CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG is enabled),將在塊的開頭和結尾處添加cookies。 分配的對象包含保護字段,以幫助調試緩沖區溢出。
2.2 Stats統計信息
在調試模式中(CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG is enabled),從池中獲取、釋放的統計信息存放在mempool結構體中。 為了避免並發訪問統計計數器,統計信息是per-lcore的。
2.3 內存對齊約束
根據X86架構上的硬件內存配置,可以通過在對象之間添加特定的填充來極大地提高性能。目的是確保每個對象的起始位置被均勻的分布在不同的channel和rank上,以便實現所有通道的負載均衡。
當執行L3轉發或流分類時,對於包緩沖區尤其如此。只訪問前64個字節,因此可以通過將對象的開始地址分布在不同的通道中來提高性能。
DIMM上的rank數目是可訪問DIMM完整數據位寬的獨立DIMM集合的數量。 由於他們共享相同的路徑,因此rank不能被同時訪問。 DIMM上的DRAM芯片的物理布局不一定與rank數目相關。
當運行app時,EAL命令行選項提供了添加內存通道和rank數目的能力。
注:命令行必須始終指定處理器的內存通道數目。
不同DIMM架構的對齊示例如下兩張圖所示 。在例子中,我們假設包是16個64字節的塊(在實際應用中這是不正確的)。
例1:Two Channels and Quad-ranked DIMM Example
例2:Three Channels and Two Dual-ranked DIMM Example
Intel® 5520芯片組有三個通道,因此,在大多數情況下,對象之間不需要填充。(除了大小為n x 3 x 64B的塊)
當創建一個新池時,用戶可以指定使用此功能。
我的疑問:
這里的例子是從dpdk官網拷貝過來的,我有個疑問,如果有誰知道麻煩給我留言。
例2中的pkt0根據圖上看明明是starts at channel 0, rank0,為什么圖上標注的是rank1?如果從圖上看,這里只保證了起始於不同channel,但仍是同一個rank而且是同一個DIMM。根據原文描述,pkt只要不是n x 3 x 64B的大小就不需要填充,感覺只是保證起始在不同channel就可以了。這個問題搜索了很久沒有滿意的答案。
2.4 本地緩存
在CPU使用率方面,由於每個訪問需要compare-and-set (CAS)操作,所以多核訪問內存池的空閑緩沖區成本比較高。 為了避免對內存池ring的訪問請求太多,內存池分配器可以維護per-core cache,並通過實際內存池中具有較少鎖定的緩存對內存池ring執行批量請求。 通過這種方式,每個core都可以訪問自己空閑對象的緩存(帶鎖), 只有當緩存填充時,內核才需要將某些空閑對象重新放回到緩沖池ring,或者當緩存空時,從緩沖池中獲取更多對象。
雖然這意味着一些buffer可能在某些core的緩存上處於空閑狀態,但是core可以無鎖訪問其自己的緩存提供了性能上的提升。
緩存由一個小型的per-core表及其長度組成。可以在創建池時啟用/禁用此緩存。
緩存大小的最大值是靜態配置,並在編譯時定義的(CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE)。
不同於per-lcore內部緩存,應用程序可以通過接口 rte_mempool_cache_create() , rte_mempool_cache_free() 和 rte_mempool_cache_flush() 創建和管理外部緩存。 這些用戶擁有的緩存可以被顯式傳遞給 rte_mempool_generic_put() 和 rte_mempool_generic_get() 。 接口 rte_mempool_default_cache() 返回默認內部緩存。 與默認緩存相反,用戶擁有的高速緩存可以由非EAL線程使用。
2.5 Mempool handlers
這允許外部存儲子系統,如外部硬件存儲管理系統和軟件存儲管理與DPDK一起使用。
mempool handler包括兩方面:
- 添加新的mempool操作代碼。這是通過添加mempool ops代碼,並使用 MEMPOOL_REGISTER_OPS 宏來實現的。
- 使用新的API調用 rte_mempool_create_empty() 及 rte_mempool_set_ops_byname() 用於創建新的mempool,並制定用戶要使用的操作。
在同一個應用程序中可能會使用幾個不同的mempool處理。 可以使用 rte_mempool_create_empty() 創建一個新的mempool,然后用 rte_mempool_set_ops_byname() 將mempool指向相關的 mempool處理回調(ops)結構體。
傳統的應用程序可能會繼續使用舊的 rte_mempool_create() API調用,它默認使用基於ring的mempool處理。 這些應用程序需要修改為新的mempool處理。
對於使用 rte_pktmbuf_create() 的應用程序,有一個配置設置(RTE_MBUF_DEFAULT_MEMPOOL_OPS),允許應用程序使用另一個mempool處理。
2.6 用例
需要高性能的所有分配器應該使用內存池實現。 以下是一些使用實例:
- Mbuf Library
- Environment Abstraction Layer
- 任何需要在程序中分配固定大小對象,並將被系統持續使用的應用程序
引用參考資料:
1)dpdk官方文檔:http://doc.dpdk.org/guides-20.02/prog_guide/mempool_lib.html
3)DDR掃盲——single rank與dual-rank:https://www.sohu.com/a/168446287_781333