- mimalloc vs HAKMEM v7 feature-by-feature 比較表 - v7-5a vs v7-5b 決定基準フレームワーク - Intrusive LIFO 採用検討 - TLS cache hit rate 目標 - Overhead 内訳の実測計画 結論: v7-5a (C6 極限最適化) を先に実施 目標: Intrusive LIFO + Headerless で mimalloc 同等性能 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
12 KiB
V7 Architecture Decision Matrix
Date: 2025-12-12 Purpose: mimalloc 競争力評価 + v7-5 方向性の決定基準を明確化
1. mimalloc vs HAKMEM v7 比較表
1-1. アーキテクチャ比較
| 要素 | mimalloc | HAKMEM v7 (C6-only) | Gap | 対策 |
|---|---|---|---|---|
| Free List 構造 | Intrusive LIFO (ブロック内 next ptr) | Page metadata explicit freelist | -2~3% | Intrusive LIFO 採用検討 |
| Size Class 数 | 128 (fine-grained) | 8 (C0-C7) | -1~2% | 現状維持 (overhead 分摊で対応) |
| Page Size | 8KB (L1 cache fit) | 64KB (ULTRA 互換) | ±0% | tradeoff 異なる (検証必要) |
| Segment Size | 4MB on-demand | 2MB fixed | ±0% | 同等 |
| Lookup Cost | Direct array O(1) | RegionIdBox binary search O(log N) | -1~2% | TLS fast path で緩和済み |
| Header | なし (headerless) | 薄く残す (1 byte) | -0.5% | v6 headerless パターン適用検討 |
| TLS Access | 1 read + array index | TLS bounds check + page_meta | -1% | TLS hint 最適化済み |
| Remote Free | Lock-free MPSC | Lock-free MPSC (同等) | ±0% | 同等 |
| Stats Overhead | Batched/deferred | Atomic increment on hot path | -0.5~1% | Stats を cold path へ移動 |
1-2. Hot Path 比較 (14 ns vs 現状)
mimalloc hot path (14 ns):
mi_heap_t* heap = mi_get_default_heap(); // 2 ns: TLS read
int cls = mi_size_to_class(size); // 3 ns: LUT + BSR
mi_page_t* page = heap->pages[cls]; // 1 ns: array index
void* p = page->free; // 3 ns: load free head
page->free = *(void**)p; // 3 ns: update free
return p; // 2 ns
HAKMEM v7 hot path (推定 18-22 ns):
SmallHeapCtx_v7* ctx = small_heap_ctx_v7(); // 2 ns: TLS read
SmallClassHeap_v7* h = &ctx->cls[class_idx]; // 1 ns: array index
SmallPageMeta_v7* p = h->current; // 2 ns: load current
void* base = p->free_list; // 3 ns: load free head
p->free_list = *(void**)base; // 3 ns: update free
p->used++; // 1 ns: counter
((uint8_t*)base)[0] = HEADER_MAGIC | class; // 2 ns: header write
small_v7_stat_alloc(); // 2 ns: atomic stats
return (uint8_t*)base + 1; // 2 ns: USER ptr
Gap 内訳:
| Step | mimalloc | v7 | Diff | 削減可能? |
|---|---|---|---|---|
| TLS access | 2 ns | 2 ns | 0 | - |
| Class lookup | 3 ns | 1 ns | -2 ns | ✅ v7 の方が速い |
| Page lookup | 1 ns | 2 ns | +1 ns | 改善可能 |
| Free list op | 6 ns | 6 ns | 0 | - |
| Counter update | 0 ns | 1 ns | +1 ns | cold path へ移動 |
| Header write | 0 ns | 2 ns | +2 ns | headerless 化 |
| Stats | 0 ns | 2 ns | +2 ns | cold path へ移動 |
| Return | 2 ns | 2 ns | 0 | - |
| Total | 14 ns | 18 ns | +4 ns | -3~4 ns 可能 |
1-3. 競争力評価
現状 v7 の立ち位置:
- Legacy baseline: 58.6M ops/s
- v7 Phase v7-3: 56.3M ops/s (-4.3%)
- mimalloc 推定: 71M ops/s (+21% vs legacy)
v7 が mimalloc に追いつくには:
- Header write 削除: +2 ns 改善
- Stats を cold path へ: +2 ns 改善
- Counter を cold path へ: +1 ns 改善
- 合計: 18 ns → 13-14 ns (mimalloc と同等)
結論: 設計上 mimalloc と 同等まで到達可能。ただし intrusive LIFO 採用が鍵。
2. v7-5 方向性の決定基準
2-1. v7-5a vs v7-5b 比較表
| 項目 | v7-5a (C6 極限最適化) | v7-5b (Multi-class 拡張) |
|---|---|---|
| 目標 | C6-only で -4.3% → ±0% | C5+C6 で overhead 分摊 |
| 作業量 | 小 (hotbox 微調整) | 中 (Segment 複数化, TLS hint 拡張) |
| リスク | 効果薄い可能性 (diminishing returns) | 複雑度増加 |
| 成功時の効果 | C6 で mimalloc 同等 | C5+C6 で -2% (予測) |
| 失敗時の影響 | 時間の無駄 (1-2日) | 設計変更のコスト (数日) |
| 検証方法 | A/B bench (C6-heavy) | Mixed bench + per-class stats |
2-2. 決定フレームワーク
v7-5a を選ぶ条件:
- C6 の hot path に明確なボトルネックがある
- Header write / Stats を cold path へ移動する具体策がある
- 1-2 日で A/B 検証可能
v7-5b を選ぶ条件:
- C6-only では overhead 削減に限界がある (既に TLS 最適化済み)
- C5/C4 の allocation が実 workload で significant
- overhead 分摊の理論値 (-4.3% → -2%) が魅力的
2-3. 推奨: v7-5a を先に試す
理由:
- 低コスト: 1-2 日で結果が出る
- 具体的改善点が明確: Header write + Stats 移動
- 失敗しても学び: 限界が分かれば v7-5b へ移行
- 成功すれば: C6 で mimalloc 同等 → 強力な武器
v7-5a タスク:
1. Header write を条件付きにする (ENV: HAKMEM_V7_HEADERLESS=1)
2. small_v7_stat_alloc() を #ifdef HAKMEM_V7_STATS で囲む
3. p->used++ を ColdIface retire 時に計算
4. A/B bench: C6-heavy (400-510B) で v7 ON/OFF
成功基準:
- C6-heavy: v7 ON が legacy ±2% 以内
- Mixed: v7 ON が legacy と同等以上
3. Intrusive LIFO 採用検討
3-1. 現状 (Explicit Freelist)
// SmallPageMeta_v7
void* free_list; // page metadata に freelist head
// Alloc
void* base = p->free_list;
p->free_list = *(void**)base;
// Free
*(void**)base = p->free_list;
p->free_list = base;
問題: free_list が page_meta 内にあり、間接参照が必要。
3-2. Intrusive LIFO (mimalloc 方式)
// Block 内に next pointer を埋め込む
// Free block:
// +0: next_ptr (8 bytes) - 次の free block へのポインタ
// +8: (unused)
// Alloc
void* p = page->free; // free head (block 先頭)
page->free = *(void**)p; // block 内の next を読む
// Free
*(void**)p = page->free; // block 内に next を書く
page->free = p;
利点:
- Zero metadata per block: free 時に block 自身に next を書く
- Cache locality: 直前に free した block は L1 cache にある
- Minimal indirection: page→free だけで完結
3-3. v7 への適用可能性
現状 v7 の構造:
SmallPageMeta_v7
├── free_list (void*) ← page_meta 内
├── used (uint32_t)
├── capacity (uint32_t)
└── ...
Block (512B for C6)
├── [0]: header (1 byte) ← USER ptr = base + 1
└── [1-511]: user data
Intrusive 化案:
SmallPageMeta_v7
├── free (void*) ← 直接 block を指す (base ptr)
├── used (uint32_t)
└── ...
Free Block (512B)
├── [0-7]: next_ptr ← intrusive next pointer
└── [8-511]: (unused when free)
Allocated Block (512B)
├── [0]: header (optional)
└── [1-511]: user data
変更点:
free_listをfreeにリネーム (semantic 変更)- free 時:
*(void**)base = page->free; page->free = base; - alloc 時:
void* p = page->free; page->free = *(void**)p; - Header は USER ptr 計算時のみ使用 (hot path から削除可能)
3-4. 採用判断
採用する場合のメリット:
- +2 ns 改善 (page_meta 間接参照削減)
- mimalloc と同じデータ構造 → 同等性能の可能性
採用しない場合の理由:
- 既存 v7 コードの変更が必要
- Header との互換性を慎重に検討必要
- v6 headerless との設計統合を先に検討すべき
結論: v7-5a の後で検討。まず Header/Stats 削除で効果を測る。
4. TLS Cache Hit Rate 目標
4-1. 現状の TLS Fast Path
// Phase v7-3 で追加
if (addr >= ctx->tls_seg_base && addr < ctx->tls_seg_end) {
// TLS hit: RegionIdBox skip
size_t page_idx = (addr - ctx->tls_seg_base) >> SMALL_PAGE_V7_SHIFT;
SmallPageMeta_v7* page = &seg->page_meta[page_idx];
// ... free logic
} else {
// TLS miss: RegionIdBox fallback
RegionLookupV6 lk = region_id_lookup_v6(ptr);
// ...
}
4-2. Hit Rate 目標
| Workload | TLS Hit Rate 目標 | 現状推定 | 根拠 |
|---|---|---|---|
| C6-heavy | 99%+ | 95-98% | ほぼ同一 segment |
| Mixed | 90%+ | 80-90% | 複数 class で分散 |
| Multi-thread | 85%+ | 75-85% | Remote free 混在 |
4-3. Hit Rate 測定方法
// ENV: HAKMEM_V7_TLS_STATS=1
static uint64_t g_v7_tls_hit = 0;
static uint64_t g_v7_tls_miss = 0;
// In free path
if (addr >= ctx->tls_seg_base && addr < ctx->tls_seg_end) {
__sync_fetch_and_add(&g_v7_tls_hit, 1);
// ...
} else {
__sync_fetch_and_add(&g_v7_tls_miss, 1);
// ...
}
// At exit
fprintf(stderr, "[V7_TLS] hit=%lu miss=%lu rate=%.2f%%\n",
g_v7_tls_hit, g_v7_tls_miss,
100.0 * g_v7_tls_hit / (g_v7_tls_hit + g_v7_tls_miss));
4-4. 改善策 (Hit Rate < 目標の場合)
-
Multi-segment TLS hint (v7-5b で必要):
struct { uintptr_t seg_base; uintptr_t seg_end; SmallSegment_v7* seg; } tls_seg[5]; // C3-C7 用 -
Last-page cache (既に設計あり):
uintptr_t last_page_base; uintptr_t last_page_end; SmallPageMeta_v7* last_page_meta; -
Segment 再利用: 同一 class の allocation が同一 segment に集中するよう誘導
5. Overhead 内訳の実測計画
5-1. 現状の仮説 (-4.3% 内訳)
| 要因 | 推定 | 対策 |
|---|---|---|
| Page metadata 間接参照 | ~2% | Intrusive LIFO |
| Extra validation | ~1% | Branch 最適化 |
| RegionIdBox fallback | ~1% | TLS cache 強化 |
| Header write | ~0.3% | Headerless 化 |
| 合計 | ~4.3% |
5-2. 実測方法
Step 1: Header write 削除
# ENV で headerless 有効化
HAKMEM_V7_HEADERLESS=1 ./bench_random_mixed_hakmem
# 効果測定: -0.3% 改善なら仮説検証
Step 2: Stats 削除
# Stats を compile-time で無効化
make clean && make CFLAGS="-DHAKMEM_V7_NO_STATS"
# 効果測定: -0.5% 改善なら仮説検証
Step 3: TLS hit rate 測定
HAKMEM_V7_TLS_STATS=1 ./bench_random_mixed_hakmem
# Hit rate < 90% なら TLS 改善が必要
Step 4: Page metadata 間接参照
# Intrusive LIFO 試験実装
# 効果測定: -2% 改善なら大きな勝利
5-3. 検証完了基準
| 仮説 | 実測結果 | 判定 |
|---|---|---|
| Header write ~0.3% | 実測値 | ±0.2% なら OK |
| Stats ~0.5% | 実測値 | ±0.3% なら OK |
| TLS fallback ~1% | Hit rate × miss cost | 計算で検証 |
| Page metadata ~2% | Intrusive 後の差分 | ±0.5% なら OK |
6. 次のアクション
6-1. 即時 (v7-5a: 1-2 日)
-
Header write 条件化:
smallobject_hotbox_v7_box.hに#ifdef HAKMEM_V7_HEADERLESS追加- Free path で header validation をスキップ
-
Stats を cold path へ:
small_v7_stat_alloc()を#ifdef HAKMEM_V7_STATSで囲む- Default OFF で bench
-
A/B bench:
- C6-heavy:
HAKMEM_BENCH_MIN_SIZE=400 HAKMEM_BENCH_MAX_SIZE=510 - v7 ON/OFF 比較
- C6-heavy:
6-2. 短期 (v7-5a 評価後: 2-3 日)
-
TLS hit rate 測定:
- Stats 追加して hit/miss ratio 確認
- < 90% なら multi-segment hint 検討
-
Intrusive LIFO 試験:
- small_heap_alloc_fast_v7 を intrusive 版に書き換え
- A/B bench で効果測定
6-3. 中期 (v7-5b 検討: 1 週間)
-
Multi-class 拡張設計:
- C5 対応の Segment 設計
- TLS context 拡張 (tls_seg[5])
-
v6 headerless 統合:
- v6 と v7 の headerless パターン統合
- RegionIdBox で class_idx 取得を共通化
7. 結論
mimalloc に勝てるか?
Yes, 設計上は可能。
条件:
- Intrusive LIFO 採用 (+2 ns)
- Header write 削除 (+2 ns)
- Stats を cold path へ (+2 ns)
- TLS hit rate 90%+ 維持
これで v7 は 13-14 ns (mimalloc 同等) に到達可能。
v7-5 の推奨
v7-5a (C6 極限最適化) を先に実施。
理由:
- 低コスト (1-2 日)
- 具体的改善点が明確
- 成功すれば v7-5b は不要かもしれない
- 失敗しても学びがある
最終目標
| Phase | 目標 | 達成基準 |
|---|---|---|
| v7-5a | C6 で legacy ±2% | C6-heavy bench |
| v7-5b | C5+C6 で legacy ±0% | Mixed bench |
| v7-6 | mimalloc 同等 | Intrusive LIFO + Headerless |
Document Status: COMPLETE Next Action: v7-5a 実装開始 Decision Owner: User