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

415 lines
12 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 を先に試す**
**理由**:
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)
```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