# 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): ```c 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): ```c 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-5b(Multi-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 16–1024B の 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 専用 (257–768B) | baseline | +4.3% | `CLASSES=0x60` | | Mixed 16–1024B | +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 更新の最小パス: - `SmallPageStatsV7`(ColdIface で retire 時に生成) → `SmallLearnerStatsV7`(per-class 累積 alloc/free/remote) → PolicyLearner(C5 alloc 比率が閾値を超えているか判定) → `SmallPolicyV7.route_kind[C5]` を `SMALL_ROUTE_V7` or `SMALL_ROUTE_MID_V3` に更新。 - 最初の版では C5 だけを対象とし、他クラス(C4/C6 など)の動的ルート選択は後続フェーズで検討する。 --- ## 3. Intrusive LIFO 採用検討 ### 3-1. 現状 (Explicit Freelist) ```c // 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 方式) ```c // 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_list` を `free` にリネーム (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 ```c // 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 測定方法 ```c // 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 で必要): ```c struct { uintptr_t seg_base; uintptr_t seg_end; SmallSegment_v7* seg; } tls_seg[5]; // C3-C7 用 ``` 2. **Last-page cache** (既に設計あり): ```c 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 削除** ```bash # ENV で headerless 有効化 HAKMEM_V7_HEADERLESS=1 ./bench_random_mixed_hakmem # 効果測定: -0.3% 改善なら仮説検証 ``` **Step 2: Stats 削除** ```bash # Stats を compile-time で無効化 make clean && make CFLAGS="-DHAKMEM_V7_NO_STATS" # 効果測定: -0.5% 改善なら仮説検証 ``` **Step 3: TLS hit rate 測定** ```bash HAKMEM_V7_TLS_STATS=1 ./bench_random_mixed_hakmem # Hit rate < 90% なら TLS 改善が必要 ``` **Step 4: Page metadata 間接参照** ```bash # 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 日) 4. **TLS hit rate 測定**: - Stats 追加して hit/miss ratio 確認 - < 90% なら multi-segment hint 検討 5. **Intrusive LIFO 試験**: - small_heap_alloc_fast_v7 を intrusive 版に書き換え - A/B bench で効果測定 ### 6-3. 中期 (v7-5b 検討: 1 週間) 6. **Multi-class 拡張設計**: - C5 対応の Segment 設計 - TLS context 拡張 (tls_seg[5]) 7. **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