核心概念
記憶體分配器是所有程式的隱形基石。每次呼叫 malloc 和 free,都依賴底層分配器在效能、記憶體效率和執行緒安全之間取得平衡。現代多核心應用的興起讓這個問題更加複雜——傳統分配器在高並發下往往成為效能瓶頸,而過激的並發優化又容易導致記憶體碎片化和過高的空間開銷。
mimalloc(發音 "mee-malloc")是由 Microsoft Research 的 Daan Leijen 開發的開源記憶體分配器,定位為 malloc/free 的直接替代品。整個實作約 12,000 行 C 程式碼,設計目標是:有界最壞情況分配時間(bounded worst-case allocation time,直至 OS 原語層)、有界空間開銷、低內部碎片化、以及最小的執行緒爭用——幾乎完全依賴原子操作實現,不使用重量級鎖。
執行緒本地堆(Thread-Local Heap)
mimalloc 的核心設計是執行緒本地堆(theap)。每個執行緒維護獨立的 theap,包含一組 mimalloc 頁面,典型頁面大小為 64 KiB。這種設計的關鍵在於:在正常操作路徑中,分配與釋放都在同一執行緒的 theap 內進行,完全無需同步原語,也不存在執行緒間爭用。
每個執行緒的 theap 按物件大小區間分類管理,分配器會根據請求大小選取對應的「物件佇列」(object queue),從中取出空閒物件。這種分層設計讓分配器在大多數情況下行為如同純本地操作。
快速路徑:小型分配的極致優化
對於小型分配(低於 1 KiB),mimalloc 實現了一條高度優化的快速路徑:
- 僅有兩個不常見分支(uncommon branches),典型情況一次分支都不需要
- 無原子操作——整個操作在編譯後僅對應數條 x64 指令
- 不需要全域鎖——完全依賴執行緒本地狀態
這使得 mimalloc 的小型分配效能接近直接操作陣列的速度,遠超傳統分配器的鎖競爭開銷。
三重自由列表策略(Three Free Lists per Page)
mimalloc 為每個頁面維護三個獨立的自由列表,這是其在多執行緒環境下同時實現低開銷與正確性的核心機制:
| 列表 | 用途 |
|---|---|
| 主分配列表 | 提供空閒物件給當前執行緒分配 |
| local_free | 接收同執行緒釋放的物件(無鎖) |
| thread_free | 接收跨執行緒釋放的物件(原子操作) |
當主分配列表耗盡時,分配器合併 local_free 並繼續服務。跨執行緒的釋放透過原子操作寫入 thread_free,定期合併回主流程。這種設計將同步開銷降至最低,同時確保記憶體不會在執行緒間無限積壓。
可擴展性與記憶體效率的平衡
以下是基準測試對比三種策略的表現(以多執行緒工作負載為基準):
| 分配器類型 | 提交記憶體 / 存活資料比 | 總分配量 |
|---|---|---|
| 系統分配器(低並發) | 1.1× | 56 GiB |
| 並發分配器(高擴展) | 4× | 262 GiB |
| mimalloc v3 | 1.3× | 262 GiB |
mimalloc v3 在總分配量與並發分配器相同的前提下,記憶體開銷僅 1.3×——接近系統分配器的 1.1×,大幅優於通用並發分配器的 4×。這展示了 mimalloc 在不犧牲擴展性的前提下,對記憶體使用的精細控制。
關鍵要點
- 輕量可嵌入:~12,000 行 C 程式碼,資料結構清晰,支援靜態連結與動態替換(
LD_PRELOAD),無需修改程式碼即可替換 malloc - 執行緒本地設計:theap 隔離消除了典型路徑下的同步需求,多核心擴展近乎線性
- 小分配極速:<1 KiB 路徑編譯為數條 x64 指令,競爭力媲美手寫陣列操作
- 記憶體效率優異:高並發工作負載下維持 1.3× 空間開銷(並發分配器通常 3-4×)
- 廣泛採用:NoGIL CPython 3.13+、Unreal Engine、《死亡擱淺》、GitHub 超 12,000 星,Rust 包裝器每日下載逾 100,000 次
- 有界最壞情況:分配時間有上界保證(直至 OS 原語),適合需要延遲保證的系統
實務應用
mimalloc 最重要的工業應用之一是與 NoGIL CPython 3.13+ 的整合。Python 移除 GIL(Global Interpreter Lock)後,多執行緒 Python 程式碼可以真正並行執行,但記憶體分配器必須支撐高並發分配釋放。mimalloc 的執行緒本地設計恰好解決了這個問題:每個 Python 執行緒的分配操作幾乎完全本地化,不會因記憶體分配成為新的並發瓶頸。
在 Unreal Engine 的採用中,mimalloc 替換了遊戲引擎原有的記憶體管理,顯著降低了遊戲主迴圈中的分配延遲。《死亡擱淺》(Death Stranding)的案例印證了 mimalloc 在實時性要求嚴苛的場景下的可靠性。
對於 AI 推論工作負載,LLM 推論過程中大量動態分配 KV Cache、中間激活張量、批次緩衝區,這些操作對分配器延遲和記憶體碎片化極為敏感。mimalloc 的低碎片化特性有助於減少 OOM 風險,執行緒本地設計則確保在多執行緒推論框架(如 vLLM)中不成為瓶頸。
相關頁面:非同步連續批次推論:LLM 推論的 CPU/GPU 並行加速 | Microsoft NSDI 2026:分散式系統與 AI 交匯的前沿突破
延伸觀點
執行緒本地堆是現代分配器的共通收斂點
mimalloc 的執行緒本地設計並非孤例。Processing-In-Memory(PIM)架構的專用分配器 PIM-malloc(arXiv 2505.13002)採用幾乎相同的兩層架構:前端的 per-thread 快取處理小型分配,後端共用 buddy allocator 處理大型請求或快取補充。這種收斂反映了一個實測結論:真實工作負載中超過 98% 的記憶體分配都在 2 KiB 以下,因此優化前端執行緒本地快取的效果,遠大於優化共用後端。PIM-malloc 軟體版本相較樸素 buddy allocator 達到 66 倍效能提升,印證了執行緒隔離的力量。
頁面竊取機制:v3 的新突破
mimalloc 最新版本(v3)引入了頁面竊取機制(page stealing):允許執行緒在自身 theap 耗盡時,無需昂貴的跨執行緒協調即可聲明其他執行緒空閒頁面的所有權。這是效能與記憶體共享之間的精妙折衷——避免了傳統「集中式共用池」的鎖競爭,同時讓記憶體在執行緒間得以流通,正是 mimalloc v3 實現 1.3× 記憶體開銷的關鍵創新之一。
Python NoGIL 的記憶體代價
來自 arXiv 的獨立研究(2603.04782)對 Python free-threaded 構建做了系統性測量:移除 GIL 後,記憶體消耗確實增加,主要來自每個物件新增的鎖結構、執行時安全機制,以及新分配器(mimalloc)的空間開銷。然而,平行化工作負載的執行速度在 16 核 ARM64 上最高快 4 倍,能耗成比例下降——這使得記憶體代價在 CPU 密集的並行任務中完全值得。順序執行工作負載則相反:無速度提升,能耗卻上升 13-43%。
對 AI 推論框架而言,這個結論的含義是:mimalloc 在多執行緒批次推論場景(多請求並發)中有明確收益,但單一序列化推論請求並不會因此獲得顯著加速。
反向連結
以下頁面引用了本頁: