C++26 std::hive 實測:取代 std::list,不取代 vector
Daniel Lemire 部落格(經 isocpp.org 轉載) · 2026-10-06 · 提案 P0447(D0447R28)
C++26 新增的 std::hive 要解決的是「元素要有穩定位址、又要能隨時刪除」時只能用 std::list 的老問題,而 Lemire 的實測顯示:它確實比 std::list 省記憶體、刪除也快得多,但跑遍歷與插入仍遠不及 std::vector。
測試以 100 萬個 uint64_t 在 GCC 16.1(-O3 -march=native)、Intel Xeon Gold 6548N 上進行。目前沒有任何標準函式庫已附帶 std::hive,作者用的是 Matt Bentley 的 plf::hive(單一標頭檔)。
原本的問題
遊戲、粒子模擬這類程式會頻繁建立與銷毀物件,而且物件之間會互相持有指標。std::vector 擴容會搬動元素、中間刪除會平移後面的元素,指標與參考全部失效。於是只能退回 std::list,換來穩定位址,代價是每個節點獨立配置、快取局部性很差。P0447 的動機就是補上 vector 與 list 之間的空缺。
核心設計
hive 由三個部件組成,元素存放在多個動態配置的區塊,擴容時只新增區塊,不搬動既有元素。
- 多個元素區塊,容量上下限可由使用者設定(
hive_limits{min, max})或由實作決定。 - skipfield:以「跳躍計數」記錄已刪除的槽位,遍歷時可在 O(1) 內跳過。
- 每個區塊內以索引串成 free list,新插入的元素重用已刪除的槽位。
提案給出的保證是單筆插入與刪除皆為均攤 O(1),迭代器為雙向(非隨機存取),插入順序不保證,因為新元素會填進舊的空洞。刪除只讓指向被刪元素的參考失效;reshape()、shrink_to_fit()、sort() 則可能重新配置,使所有參考失效。
std::hive<Particle> h;
auto it = h.insert(p); // 回傳 iterator
Particle* ptr = &*it; // 位址之後保持有效
h.erase(other); // ptr 不受影響
auto it2 = h.get_iterator(ptr); // 指標轉回 iterator實測數字
以下為 Lemire 文章所列,單位為每元素奈秒(記憶體為每個 8 位元組元素的位元組數)。
| 項目 | std::vector | std::hive | std::list |
|---|---|---|---|
| 插入 | 0.81(reserve 後 0.29) | 1.57(reserve 後 1.76) | 14.22 |
| 遍歷 | 0.22 | 1.77 | 1.51 |
| 刪除一半元素 | 3.0 | 2.1 | 77.4 |
| 記憶體(位元組) | 8.0(緊密) | 9.4 | 32.0 |
有兩個細節值得注意。遍歷時 hive(1.77)略慢於 list(1.51),因為要處理 skipfield;它勝在插入、刪除與記憶體。另外 hive 預先 reserve 後插入反而是 1.76,比未 reserve 的 1.57 慢,來源文章未解釋原因。
影響範圍
如果你的程式現在用 std::list、或用 vector 搭配「標記刪除」「swap-and-pop」再手動維護索引表,hive 是值得評估的替代品,尤其是物件互相以指標引用、且不在乎順序的場景(實體系統、粒子、連線池)。刪除一半元素的成本約為 list 的三十分之一(2.1 對 77.4),記憶體約為三分之一(9.4 對 32.0)。
相反地,若熱點是順序遍歷或批次插入,vector 的 0.22 對 hive 的 1.77 差距明顯,不應替換。Lemire 的結論是 hive 是「好得多的 std::list」,而非 vector 的替代。
實務上要注意兩點:目前沒有標準函式庫實作,要試用須引入 plf::hive;而且因為順序不保證,依賴插入順序的程式不能直接換。以上數字只來自單一 CPU 與單一編譯器、元素為 8 位元組整數,換成較大的物件或不同的刪除比例,結果文章並未涵蓋。
原始來源:Lemire:How fast is C++26's std::hive?、P0447R28 std::hive 提案、isocpp.org 轉載