# MemProf: a Memory Profiler for NUMA Multicore Systems
source code: https://github.com/Memprof/module
### 目標:
這篇論文是在開發 MemProf 工具,這個工具可以用於分析在 [[NUMA]] 架構底下的應用程式記憶體使用分析,可以提供應用程式在存取遠端記憶體時涉及的 [[thread]] 以及物件的資訊
### 分析的應用程式:
四個應用程式,分別為 FaceRec, Streamcluster, Psearchy, Apache
### 什麼樣的 workload 下 MemProf 表現得不錯?
具有任意生命週期的物件或是不同粒度大小的物件,或是應用程式對於記憶體的存取模式會隨著時間變化的,記憶體管理使用自定義策略的應用程式
### 為什麼需要 MemProf:
NUMA 相比起 UMA,會有記憶體存取的問題,如果我們嘗試存取不在目前 group 底下的記憶體,而是去存取其他 group 底下的記憶體,就會有顯著的時間開銷,換言之,對於 NUMA 架構下執行的應用程式,遠端存取記憶體的操作會嚴重的影響性能,舉例來說,在 node 2 上面執行的 process 去存取 node 1 的記憶體,這是論文中對於遠端存取的定義。而 group 的定義在 Linux 中 node 的定義是相同的,在傳統的 profiler 如 OProfile, Linux Perf, VTune, Memphis 等工具沒有提供關於 NUMA 記憶體存取的資訊,也就是應用程式在哪一個節點上執行,在某個時間點存取了什麼物件,且這個物件位於哪一個節點。
雖然上面提及的這一些 profiler 有一些可以提供關於 global static memory object 的存取資訊,但是對於實際上的 workload 而言,這一些物件存取比率佔據所有記憶體存取事件中不到 4% 的比例,而對於非 global static memory object 的存取,上面提到的這一些分析器使用的方式是找一個目標記憶體地址,也許這個記憶體地址關連到某一個物件,然後追蹤這個記憶體地址涉及的存取指令。
而 MemProf 相比起上面這一些工具,可以針對一個遠端的記憶體地址在被存取時所關聯的 thread 以及 object
### MemProf 實作部分
實作上依賴於 kernel module 用來蒐集資訊,以及使用一些 user space 上的 library 作一些資訊塞選,filiter 的操作。對於監聽記憶體存取事件,MemProf 使用 Instruction-Based Sampling (ISB) 的技術進行採樣
### 定義應用程式對於 NUMA 性能負面影響的存取模式
![[Pasted image 20241118211951.png]]
- 第一種情況 (Remote usage after allocation) :當一個物件的記憶體是由 T1 所分配,分配的物件位於 N1 上,接著之後這物件被 T2 所存取,並在 N2 上面執行,這種存取模式通常會出現在具有生產者,消費者模型的應用程式中。要優化這個情況最簡單的方式就是直接把物件的記憶體分配在 N2 上,通常這種解法需要使用 NUMA-aware allocation functions 來達成,另外一種方式就是在 T2 開始存取資料之前通過 system call 去搬移資料,前提是不能夠帶來更大的 overhead 或是可以平均的分攤掉這個 overhead,像是 `move_pages`
- 第二種情況 (Alternate remote accesses to an object): 有多個 thread 隨時間變化去存取一個物件,以圖上的例子是有 T1, T2, T3 等等,而物件位於 N1 上,一個可以使用的作法是將 T1, T2, T3 綁定到 N1 上面執行,這一點可以用 CPU affinity 設定,如此可以避免 remote memory access,具體的作法為 thread 的 CPU affinity 設置只能在 N1 上面的 4 個核心上面執行,假設 N1 上面有 4 個核心,則 T1, T2, T3 嘗試存取該物件時,因為 thread 綁定在 N1 上,記憶體存取屬於本地存取。或是如果存取有一定的時間規律性,我們預測並根據時間預先對物件進行遷移
- 第三種情況 (Concurrent remote accesses to an object): 一個物件在一個時間段被多個執行序並行存取,如果我們使用之前的優化方式,直接將 T2 綁定在 T1 上面執行,可能會發生負載嚴重不平衡的情況。另外一種方式就是將該物件複製到多個記憶體節點上,但也會延伸出我們需要同步多個物件的問題,這個方法可能適用於唯獨物件中才能夠使用。如果 N1 的記憶體控制器已經飽和 (這一點可以通過評估計算記憶體的平均存取延遲時間來探測),則可以考慮兩種最佳化的方式。第一個是平衡多個記憶體節點上不同最熱物件的分配,達成負載平衡,但是如果這樣的飽和情況是因為幾個大物件所引起的,則可可以將某一個這個 class 所分配的物件交錯分配在不同節點上,這樣優化可能可以降低記憶體存取延遲,或是盡可能的保持低延遲
### 在 NUMA 架構下的應用程式,主要會與性能相關的物件以及行為
- 全域靜態分配的物件
- 動態分配的物件
- 記憶體映射物件
- 作業系統映射的二進位檔案的部份,如某一些 binary 或是動態函式庫
- thread 的 stacks
### Object lifecycle tacking
關於物件和 thread 的生命週期以及記憶體存取的追蹤方式,為 overload 記憶體相關分配函式,如 (`malloc`, `calloc`, `realloc`, `free`, `mmap`, `mummap`) 等等函式,如此就可以進行動態追蹤,具體的實做方式是通過 `LD_PRELOAD` 以及 dlsym 將要分析的應用程式與 MemProf 提供的 share library 連接所執行。
對於 code sections 以及 global static variables 的生命週期追蹤,MemProf 實做的方式是通過 kernel module 進行處理,這個 kernel module 會 overload `perf_event_mmap` 這個函式,之所以選擇 overload 這個函式,是因為每一次 process 建立的時候,當 binary 或是 library 要映射到該 process 的記憶體空間時都會呼叫此函式。
:::info
做 profiling 時,可以使用 kernel module 搭配 `perf_event` 系列的 API 去得到一些資訊
:::
### AMD IBS 採樣
### 關於 overhead
主要開銷來自於 IBS 採樣頻率,對於採樣等等,需要去測量需要多少週期
另外一個開銷來自於 trace user lib 和 kernel module trace 所帶來的開銷,攔截 user lib 並處理需要大約 400 的 clock,需要計算儲存這一些事件需要多大的緩衝區。
### 關於 NUMA Profile 的相關工具
Memphis: NUMA 效能問題分析器,依賴於 IBS,主要找遠端存取的記憶體
VTune: 使用 PEBS,主要找遠端存取的記憶體地址
Dprof: 找到不良快取行為的物件
Oprofile 和 Perf: 使用 performance counter 去定位遠端存取的函式或是 asm code.
Linux Kernel 裡面改進 NUMA 效能的 API 包含 cpusets 可以讓應用程式強制執行全域記憶體的策略,概念上就是強制硬程式在一有限的節點集合中分配記憶體,或是前面提到的 `move_pages` 可以用來做物件的遷移等等。
在考慮記憶體遷移問題時,最好也順便遷移他的 working set。
### 論文結論:
NUMA 架構底下,應用程式中遠端存取記憶體會有大量的開銷,MemProf 可以在上面所敘述的 workload 底下表現良好,MemProf 的限制為很依賴於程式設計師對於問題的判定能力以及 MemProf 主要適合應用在 cache 效率低且大量記憶體存取的應用程式
為什麼這一些 workload 足夠有代表性,這一篇論文在方法論上面有什麼創新? benchmark 是怎麼做的? 稍微掃過一次他的實作,感覺裡面有很多目前 eBPF 實作上使用到的技術