[英]Profiling resident memory usage and many page faults in C++ program on linux
我試圖弄清楚為什么我的一個程序版本(“新”)的常駐 memory 比同一程序的另一個版本(“基線”)高得多(5x)。 該程序在具有 E5-2698 v3 CPU 並用 C++ 編寫的 Linux 集群上運行。 基線是多進程程序,新的是多線程程序; 它們基本上都在執行相同的算法、計算和操作相同的輸入數據等。在兩者中,進程或線程的數量與內核 (64) 一樣多,線程固定在 CPU 上。 我已經使用 Valgrind Massif 和 Heaptrack 進行了大量的堆分析,它們表明 memory 分配是相同的(應該是)。 該程序的基線和新版本的 RSS 都大於 LLC。
該機器有 64 個內核(超線程)。 對於這兩個版本,我strace
d 相關過程並發現了一些有趣的結果。 這是我使用的 strace 命令:
strace -k -p <pid> -e trace=mmap,munmap,brk
以下是有關這兩個版本的一些詳細信息:
基線版本:
新版本
memcpy
的默認設置進行了大量的大型緩沖區(25MB)的memcpy
調用(我認為應該使用非臨時存儲,但我還沒有驗證這一點)mmap
和munmap
調用。 奇怪的是,在調試模式下沒有生成任何東西。 (更多內容見下文)。 假設我沒看錯,與基線版本相比,新版本的 RSS 總量(整個節點)高出 5 倍,並且使用 perf stat 測量的頁面錯誤明顯更多。 當我對 page-faults 事件運行 perf record/report 時,它顯示所有頁面錯誤都來自程序中的 memset。 但是,基線版本也具有該 memset,並且沒有因此而導致的頁面錯誤(使用perf record -e page-faults
進行驗證)。 一個想法是,由於某種原因,還有其他一些 memory 壓力導致 memset 出現頁面錯誤。
所以,我的問題是我如何理解常駐 memory 的大幅增加來自哪里? 是否有性能監視器計數器(即性能事件)可以幫助闡明這一點? 或者,是否有類似 heaptrack 或 massif 的工具可以讓我查看構成 RES 足跡的實際數據是什么?
我在四處尋找時注意到的最有趣的事情之一是上面提到的mmap
和munmap
調用的不一致。 基線版本沒有生成任何這些; 新版本的配置文件和發布版本(基本上是-march=native
和-O3
)確實發出了這些系統調用,但新版本的調試版本沒有調用mmap
和munmap
(超過數十秒的跟蹤)。 請注意,應用程序基本上是分配一個數組,進行計算,然后釋放該數組——所有這些都在一個運行多次的外循環中。
在某些情況下,分配器似乎能夠輕松地重用先前外部循環迭代中分配的緩沖區,但在其他情況下則不然——盡管我不明白這些事情是如何工作的,也不知道如何影響它們。 我相信分配器有一個時間 window 的概念,之后應用程序 memory 將返回給操作系統。 一種猜測是,在優化的代碼(發布版本)中,向量化指令用於計算,它使其速度更快。 這可能會改變程序的時序,從而將 memory 返回給操作系統; 盡管我不明白為什么基線中沒有發生這種情況。 也許線程正在影響這一點?
(作為一個暗中評論,我還要說我嘗試了 jemalloc 分配器,既使用了默認設置,也更改了它們,新版本的速度降低了 30%,但沒有改變使用 jemalloc 時的基線。我在這里有點驚訝,因為我以前使用 jemalloc 的經驗是它往往會在多線程程序中產生一些加速。我添加這個評論以防它觸發其他一些想法。)
一般來說:GCC 可以將 malloc+memset 優化為 calloc,從而使頁面保持不變。 如果您實際上只觸及大分配的幾頁,那么沒有發生這種情況可能會導致頁面錯誤的巨大差異。
或者版本之間的變化是否可能讓系統以不同的方式使用透明的大頁面,而這種方式恰好不適合您的工作負載?
或者,也許只是不同的分配/免費使您的分配器手冊頁回到操作系統,而不是將它們保留在空閑列表中。 延遲分配意味着您在從 kernel 獲取頁面后第一次訪問頁面時會出現軟頁面錯誤。 strace
查找mmap
/ munmap
或brk
系統調用。
在您的特定情況下,您的strace
測試確認您的更改導致malloc
/ 將頁面free
交給操作系統,而不是將它們保留在空閑列表中。
這充分解釋了額外的頁面錯誤。 munmap 調用的回溯可以識別有罪的免費調用。 To fix it, see https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html / http://man7.org/linux/man-pages/man3/mallopt.3 .html ,尤其是M_MMAP_THRESHOLD
(也許提高它以獲得 glibc malloc 不要為您的 arrays 使用 mmap?)。 我之前沒有玩過參數。 手冊頁提到了有關動態 mmap 閾值的內容。
它沒有解釋額外的 RSS; 你確定你不是不小心分配了 5 倍的空間嗎? 如果你不是,也許更好的 alignment 分配讓 kernel 使用以前沒有的透明大頁,可能導致在數組末尾浪費多達 1.99 MiB 而不是不到 4k? 或者,如果您只分配超過 2M 邊界的前幾個 4k 頁,Linux 可能不會使用大頁。
如果您在memset
中遇到頁面錯誤,我假設這些 arrays 並不稀疏,並且您正在觸摸每個元素。
我相信分配器有一個時間 window 的概念,之后應用程序 memory 返回到操作系統
分配器可以在您每次調用free
時檢查當前時間,但這很昂貴,因此不太可能。 他們也不太可能使用信號處理程序或單獨的線程來定期檢查空閑列表大小。
我認為 glibc 只是使用基於大小的啟發式算法,它會評估每個free
。 正如我所說,手冊頁提到了一些關於啟發式的內容。
IMO 實際調整 malloc (或找到不同的 malloc 實現)更適合您的情況可能應該是一個不同的問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.