jemalloc、tcmalloc 與 mimalloc
/ JEM-alloc, TEE-see-malloc, MY-malloc /
想像一個受歡迎的小吃攤,只有一位收銀員。午餐尖峰一到,一百個人同時想點餐,他們全都排在那一位收銀員前面等。一個天真的配置器在許多執行緒同時呼叫 malloc() 時,大致就是這樣:它們全都爭搶一個受單一鎖保護的共享堆積,執行緒只能互相等待而停滯。jemalloc、tcmalloc 與 mimalloc 是三個工業級的替代配置器,主要就是為了移除這個瓶頸而設計,讓配置能在許多核心上擴展。
它們共用一套由這個領域的想法構成的架構。第一,執行緒快取(tcmalloc 的 tc 字面就是 thread-caching):每個執行緒為每個 size class 保有一小份私有的空閒區塊存貨,於是壓倒性常見的情況——一次小型 malloc 或 free——只碰執行緒區域記憶體,完全不需要鎖。第二,中央堆積(或一組 arena):當某執行緒的快取空了,它就從共享結構批次補貨;當快取滿出,就批次刷回去;jemalloc 尤其給每個執行緒指派數個 arena 之一以分散爭用。第三,size class 與分離式儲存把一切組織起來,並為大型配置另闢一條直接交給作業系統的路徑。差異在於側重點:tcmalloc(Google)首創每執行緒快取加上中央自由串列;jemalloc(原由 Jason Evans 開發,在 Facebook/FreeBSD 大量使用)著重多個 arena 與強力的碎片控制;mimalloc(Microsoft)使用分片的自由串列與乾淨的自由串列設計,以求極低的開銷。
為何重要:在一台多執行緒伺服器上,把系統 malloc 換成其中之一,往往能在不改一行程式碼的情況下(你只要連結或預載它)大幅降低鎖爭用與尾端延遲。誠實的定位:它們不是魔法、也並非總是更好——對單執行緒程式,系統配置器可能一樣好;它們在執行緒快取中用掉更多記憶體;正確的選擇取決於你工作負載的配置模式。它們是整個領域詞彙——執行緒快取、中央堆積、size class、arena——組裝起來、用以大規模解決配置器爭用問題的實作範例。
# 啟動時替換系統 malloc,免重新編譯: $ LD_PRELOAD=/usr/lib/libtcmalloc.so ./my_server # 或直接連結它: $ gcc -O2 main.c -ljemalloc -o my_server # 熱路徑:每執行緒快取的取出/推入,「不」取鎖
可直接替換:每一個都給每個執行緒一份無鎖的私有快取,讓並行 malloc 能跨核心擴展。
這些配置器降低的是爭用而非工作量:單執行緒工作負載可能收益甚微,而執行緒快取以額外記憶體換取速度。切換前務必在你的真實工作負載上量測。