Files
hakmem/docs/analysis/V7_ARCHITECTURE_DECISION_MATRIX.md
Moe Charm (CI) 580e8f57f7 docs: V7 Architecture Decision Matrix (mimalloc 競争力評価)
- 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>
2025-12-12 04:36:37 +09:00

12 KiB
Raw Blame History

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 に追いつくには:

  1. Header write 削除: +2 ns 改善
  2. Stats を cold path へ: +2 ns 改善
  3. Counter を cold path へ: +1 ns 改善
  4. 合計: 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 を選ぶ条件:

  1. C6 の hot path に明確なボトルネックがある
  2. Header write / Stats を cold path へ移動する具体策がある
  3. 1-2 日で A/B 検証可能

v7-5b を選ぶ条件:

  1. C6-only では overhead 削減に限界がある (既に TLS 最適化済み)
  2. C5/C4 の allocation が実 workload で significant
  3. overhead 分摊の理論値 (-4.3% → -2%) が魅力的

2-3. 推奨: v7-5a を先に試す

理由:

  1. 低コスト: 1-2 日で結果が出る
  2. 具体的改善点が明確: Header write + Stats 移動
  3. 失敗しても学び: 限界が分かれば v7-5b へ移行
  4. 成功すれば: 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

変更点:

  1. free_listfree にリネーム (semantic 変更)
  2. free 時: *(void**)base = page->free; page->free = base;
  3. alloc 時: void* p = page->free; page->free = *(void**)p;
  4. 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 < 目標の場合)

  1. Multi-segment TLS hint (v7-5b で必要):

    struct {
        uintptr_t seg_base;
        uintptr_t seg_end;
        SmallSegment_v7* seg;
    } tls_seg[5];  // C3-C7 用
    
  2. Last-page cache (既に設計あり):

    uintptr_t last_page_base;
    uintptr_t last_page_end;
    SmallPageMeta_v7* last_page_meta;
    
  3. 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 日)

  1. Header write 条件化:

    • smallobject_hotbox_v7_box.h#ifdef HAKMEM_V7_HEADERLESS 追加
    • Free path で header validation をスキップ
  2. Stats を cold path へ:

    • small_v7_stat_alloc()#ifdef HAKMEM_V7_STATS で囲む
    • Default OFF で bench
  3. A/B bench:

    • C6-heavy: HAKMEM_BENCH_MIN_SIZE=400 HAKMEM_BENCH_MAX_SIZE=510
    • v7 ON/OFF 比較

6-2. 短期 (v7-5a 評価後: 2-3 日)

  1. TLS hit rate 測定:

    • Stats 追加して hit/miss ratio 確認
    • < 90% なら multi-segment hint 検討
  2. Intrusive LIFO 試験:

    • small_heap_alloc_fast_v7 を intrusive 版に書き換え
    • A/B bench で効果測定

6-3. 中期 (v7-5b 検討: 1 週間)

  1. Multi-class 拡張設計:

    • C5 対応の Segment 設計
    • TLS context 拡張 (tls_seg[5])
  2. v6 headerless 統合:

    • v6 と v7 の headerless パターン統合
    • RegionIdBox で class_idx 取得を共通化

7. 結論

mimalloc に勝てるか?

Yes, 設計上は可能

条件:

  1. Intrusive LIFO 採用 (+2 ns)
  2. Header write 削除 (+2 ns)
  3. Stats を cold path へ (+2 ns)
  4. 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