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

470 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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-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 更新の最小パス:
- `SmallPageStatsV7`ColdIface で retire 時に生成)
`SmallLearnerStatsV7`per-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)
```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