Files
hakmem/docs/analysis/V7_ARCHITECTURE_DECISION_MATRIX.md
Moe Charm (CI) 79674c9390 Phase v10: Remove legacy v3/v4/v5 implementations
Removal strategy: Deprecate routes by disabling ENV-based routing
- v3/v4/v5 enum types kept for binary compatibility
- small_heap_v3/v4/v5_enabled() always return 0
- small_heap_v3/v4/v5_class_enabled() always return 0
- Any v3/v4/v5 ENVs are silently ignored, routes to LEGACY

Changes:
- core/box/smallobject_hotbox_v3_env_box.h: stub functions
- core/box/smallobject_hotbox_v4_env_box.h: stub functions
- core/box/smallobject_v5_env_box.h: stub functions
- core/front/malloc_tiny_fast.h: remove alloc/free cases (20+ lines)

Benefits:
- Cleaner routing logic (v6/v7 only for SmallObject)
- 20+ lines deleted from hot path validation
- No behavioral change (routes were rarely used)

Performance: No regression expected (v3/v4/v5 already disabled by default)

Next: Set Learner v7 default ON, production testing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 06:09:12 +09:00

16 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 完了C6 v7 Hot path stats 削除)

理由:

  1. 低コスト: 1-2 日で結果が出る
  2. 具体的改善点が明確: Header write + Stats 移動
  3. 失敗しても学び: 限界が分かれば v7-5b へ移行
  4. 成功すれば: C6 で mimalloc 同等 → 強力な武器

実装内容 (Phase v7-5a, 完了済み):

  1. C6 v7 Hot path から per-page stats 更新を削除。
    • alloc_count++ / free_count++ / live_current++/-- を ColdIface 経路refill/retire 時)に移動。
    • Hot path での stats は HAKMEM_V7_HOT_STATS=1 のときだけ ENV ゲートで有効(デフォルト OFF
  2. Header-at-carve-time の完全移行は見送り。
    • freelist が block[0] を next pointer として使っており、「carve 時だけ header write」は v7 現行構造では安全にできないため、ヘッダは引き続き alloc 時に 1 byte 書く前提を維持。

A/B 結果 (C6-heavy):

Metric v7 OFF (MID v3) v7 ON (v7-5a)
Throughput (avg) 9.26M ops/s 9.27M ops/s
Delta baseline +0.15%

→ 目標だった -4.3% → ±2% を達成し、C6-only v7 は MID v3 とほぼ同等±0%)になった。

次の判断軸:

  • v7-5bMulti-class 拡張に進むかどうかは、「C5/C4 を v7 に載せて overhead を分摊する価値があるか」の評価次第。
  • Intrusive LIFO / headerless / Stats cold path などは v7-5a の結果を踏まえて、別フェーズで改めて検討する。

2-4. 実績: v7-5b 完了C5+C6 multi-class 拡張)

実装内容 (Phase v7-5b, 完了済み):

  • SmallSegment_v7 / SmallObjectColdIface_v7 / SmallObjectHotBox_v7 を C5+C6 の 2 クラス対応に拡張。
    • SMALL_V7_CLASS_SUPPORTED() に C5 を追加。
    • small_v7_block_size() を C5/C6 両クラスを扱う switch に拡張。
    • HotBox 側の alloc/free class validation を C5+C6 対応に更新。
  • TLS 構造は C6 lane を維持しつつ、C5 については v7 small コアに乗せるが、C6 TLS のキャッシュ線を肥大化させない形をキープ。

A/B 結果 (C5+C6 v7 プロファイル):

Config Avg Throughput Delta
C6-only v7 7.64M ops/s baseline
C5+C6 v7 7.97M ops/s +4.3%
  • C6 性能維持: C6-only v7 と比べて劣化なし)
  • C5 net positive: +4.3%
  • TLS bloat: なしC6 TLS lane のヒット率に悪影響なし)

今後の方向性:

  • C4 は現在 ULTRA 本線で十分速いため、v7 に載せるかどうかは別途慎重に判断Phase v7-5c 候補)。
  • v7 small/mid コアは C5+C6 2 クラス対応でも破綻しないことが分かったので、次は Mixed 161024B の A/B や Learner 連携(動的 route 選択)の設計・検証に進む。

3. C5 ルート選択と Learner 連携の必要性

Mixed / C5+C6 専用での A/B から、C5 v7 の利得が workload-dependent であることが判明した:

Workload C6-only v7 C5+C6 v7 推奨設定
C5+C6 専用 (257768B) baseline +4.3% CLASSES=0x60
Mixed 161024B +0.5% -8.0% CLASSES=0x40

結論:

  • C5 は C5-heavy な専用 workload では v7 が有利だが、一般的な Mixed では MID v3+ULTRA の方が全体として速い。
  • 「ENV で C5 を常に v7 にする / 常に MID v3 にする」ような固定方針は現実的でなく、Learner で C5 の route を動的に切り替える設計が必要。

設計メモv7-7 以降):

  • Stats → Learner → Policy route_kind 更新の最小パス:
    • SmallPageStatsV7ColdIface で retire 時に生成)
      SmallLearnerStatsV7per-class 累積 alloc/free/remote
      → PolicyLearnerC5 alloc 比率が閾値を超えているか判定)
      SmallPolicyV7.route_kind[C5]SMALL_ROUTE_V7 or SMALL_ROUTE_MID_V3 に更新。
  • 最初の版では C5 だけを対象とし、他クラスC4/C6 など)の動的ルート選択は後続フェーズで検討する。

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