# CURRENT TASK (Phase 14–26 Snapshot) – Tiny / Mid / ExternalGuard / Unified Cache / Front Gate **Last Updated**: 2025-11-17 **Owner**: ChatGPT → Phase 23/25/26 実装完了: Claude Code **Size**: 約 350 行(Claude 用コンテキスト簡略版) --- ## 🎉 **Phase 26: Front Gate Unification - 完了** (2025-11-17) **成果**: Random Mixed 256B ベンチマーク **+12.9%** 改善 (11.33M → 12.79M ops/s) ### Phase 26: Front Gate Unification (ChatGPT先生提案) - **設計**: malloc → hak_alloc_at (236行) → wrapper → tiny_alloc_fast の **3層オーバーヘッド削減** - **実装**: `core/front/malloc_tiny_fast.h` + `core/box/hak_wrappers.inc.h` 統合 - **戦略**: Tiny範囲(≤1024B)専用の単層直行経路、Phase 23 Unified Cache 活用 - **ENV**: `HAKMEM_FRONT_GATE_UNIFIED=1` でデフォルトOFF → **本番投入推奨** ### Phase 26 実装詳細 **malloc_tiny_fast()** (alloc fast path): ```c 1. size → class_idx (inline table lookup, 1-2 instructions) 2. unified_cache_pop_or_refill(class_idx) (Phase 23 tcache, 2-3 cache misses) 3. Write header + return USER pointer (2-3 instructions) Total: 8-10 instructions (vs 3-layer cascade: 236 lines routing + diagnostics) ``` **free_tiny_fast()** (free fast path): ```c 1. Page boundary guard (offset_in_page == 0 → return 0) 2. Read header + validate Tiny magic (0xa0-0xa7) 3. unified_cache_push(class_idx, base) (Phase 23 tcache, 2-3 cache misses) Total: 6-8 instructions (vs classify_ptr + hak_free_at routing) ``` ### Phase 26 修正したバグ 1. **初期化バグ**: Phase 26 fast path が hak_init() をバイパス → `if (!g_initialized) hak_init()` 追加 2. **ページ境界SEGV**: free_tiny_fast() がページ先頭 (offset==0) で前ページ読み → ガード追加 ```c uintptr_t off = (uintptr_t)ptr & 0xFFFu; if (off == 0) return 0; // Page-aligned → 通常 free 経路へ ``` ### A/B ベンチマーク結果 (Random Mixed 256B, 100K iterations) | Configuration | Run 1 | Run 2 | Run 3 | **Average** | vs Baseline | |---------------|-------|-------|-------|-------------|-------------| | **Phase 26 OFF** | 11.21M | 11.02M | 11.76M | **11.33M ops/s** | Baseline | | **Phase 26 ON** | 13.21M | 12.55M | 12.62M | **12.79M ops/s** | **+12.9%** 🎯 | **ChatGPT先生の予測**: +10-15% (3層オーバーヘッド削減による改善) **実測結果**: **+12.9%** ← **予測ど真ん中!** 🎯 ### 本番推奨設定 (Phase 23 + Phase 26 組み合わせ) ```bash export HAKMEM_TINY_UNIFIED_CACHE=1 # Phase 23: Hot_2048がデフォルト export HAKMEM_FRONT_GATE_UNIFIED=1 # Phase 26: Front Gate Unification ./out/release/bench_random_mixed_hakmem # Expected: 12.79M ops/s (+27.8% vs Phase 23前のbaseline 10.0M ops/s) ``` **主要ファイル**: - `core/front/malloc_tiny_fast.h` - Phase 26 single-layer malloc/free implementation - `core/box/hak_wrappers.inc.h:128-143` - Phase 26 fast path integration (malloc) - `core/box/hak_wrappers.inc.h:179-190` - Phase 26 fast path integration (free) --- ## 🎉 **Phase 23/25: Unified Frontend Cache - 完了** (2025-11-17) **成果**: Random Mixed 256B ベンチマーク **+7.3%** 改善 (10.58M → 11.35M ops/s) ### Phase 23: Unified Cache Implementation - **設計**: tcache-style single-layer frontend (Ring → FastCache → SFC → SLL の 4 層を 1 層に統合) - **実装**: `core/front/tiny_unified_cache.{h,c}` - Array-based ring buffer (2-3 cache misses) - **統合**: Alloc path (`tiny_alloc_fast.inc.h:621-633`) + Free path (`hak_free_api.inc.h`) - **ENV**: `HAKMEM_TINY_UNIFIED_CACHE=1` でデフォルトOFF → **Hot_2048設定で本番投入** ### Phase 23 Capacity Optimization (Hot_2048) - **Task Agent**: 10 configurations × 3 runs = 35 benchmarks - **最適設定**: C2/C3 (128B/256B) = 2048 slots, 他 = 64 slots - **根拠**: Hot-class優先戦略が+6.2%の追加改善(vs All_128) - **メモリ**: ~1.1MB cache overhead (C2/C3 に集中配置) ### Phase 25-A: Header Read Optimization (+2.2%) - **削減**: FG_DOMAIN_TINY の重複 header read を除去 - **L1 hit**: 2回目の header read は L1 cache hit (~1 cycle) → 効果限定的 ### Phase 25-B-1: Promote-on-Full (REVERTED, -4.0%) - **失敗**: Smart promotion logic が overhead > benefit - **教訓**: Clever ≠ Fast、incremental最適化は限界に達した ### Debug Log修正 (性能改善) - **修正箇所**: `core/tiny_refill_opt.h:316-326`, `core/box/ss_hot_prewarm_box.c:143-146` - **問題**: `[C2_CARVE]` / `[BOX_SS_HOT_PREWARM]` が Release build で常時出力 - **解決**: `#if !HAKMEM_BUILD_RELEASE` で囲み、stderr負荷を除去 ### 本番推奨設定 ```bash export HAKMEM_TINY_UNIFIED_CACHE=1 # Hot_2048がデフォルト(C2/C3=2048, 他=64) ./out/release/bench_random_mixed_hakmem ``` **次の戦略**: Phase 23でfrontend最適化は限界、Phase 12 Shared SuperSlab Pool (backend根本解決) へ進む --- ## 1. 全体の現在地(どこまで終わっているか) - Tiny (0–1023B) - NEW 3-layer front(bump / small_mag / slow)安定。 - TinyHeapV2: 「alloc フロント+統計」は実装済みだが、実運用は **C2/C3 を UltraHot に委譲**。 - TinyUltraHot(Phase 14): - C2/C3(16B/32B)専用 L0 ultra-fast path(Stealing モデル)。 - 固定サイズベンチで +16〜36% 改善、hit 率 ≈ 100%。 - Box 分離(Phase 15): - free ラッパが外部ポインタまで `hak_free_at` に投げていた問題を修正。 - BenchMeta(slots など)→ 直接 `__libc_free`、CoreAlloc(Tiny/Mid)→ `hak_free_at` の二段構えに整理。 - Mid / PoolTLS(1KB–32KB) - PoolTLS Phase 完了(Mid-Large MT ベンチ) - ~10.6M ops/s(system malloc より速い構成あり)。 - lock contention(futex 68%)を lock-free MPSC + bind box で大幅削減。 - GAP 修正(Tiny 1023B / Mid 1KB〜): - `TINY_MAX_SIZE=1023` / `MID_MIN_SIZE=1024` で 1KB–8KB の「誰も扱わない帯」は解消済み。 - Shared SuperSlab Pool(Phase 12 – SP-SLOT Box) - 1 SuperSlab : 多 class 共有 + SLOT_UNUSED/ACTIVE/EMPTY 追跡。 - SuperSlab 数: 877 → 72(-92%)、mmap/munmap: -48%、Throughput: +131%。 - Lock contention P0-5 まで実装済み(Stage 2 lock-free claiming)。 - ExternalGuard(Phase 15) - UNKNOWN ポインタ(Tiny/Pool/Mid/L25/registry どこでも捕まらないもの)を最後の箱で扱う。 - 挙動: - `hak_super_lookup` など全て miss → mincore でページ確認 → 原則「解放せず leak 扱い(安全優先)」。 - Phase 15 修正で: - BenchMeta のポインタを CoreAlloc に渡さなくなり、UNKNOWN 呼び出し回数が激減。 - `mincore` の CPU 負荷もベンチではほぼ無視できるレベルまで縮小。 --- ## 2. Tiny 性能の現状(Phase 14–15 時点) ### 2.1 Fixed-size Tiny ベンチ(HAKMEM vs System) **Phase 21-1: Ring Cache Implementation (C2/C3/C5) (2025-11-16)** 🎯 - **Goal**: Eliminate pointer chasing in TLS SLL by using array-based ring buffer cache - **Strategy**: 3-layer hierarchy (Ring L0 → SLL L1 → SuperSlab L2) - **Implementation**: - Added `TinyRingCache` struct with power-of-2 ring buffer (128 slots default) - Implemented `ring_cache_pop/push` for ultra-fast alloc/free (1-2 instructions) - Extended to C2 (32B), C3 (64B), C5 (256B) size classes - ENV variables: `HAKMEM_TINY_HOT_RING_ENABLE=1`, `HAKMEM_TINY_HOT_RING_C2/C3/C5=128` - **Results** (`bench_random_mixed_hakmem 500K, 256B workload`): - **Baseline** (Ring OFF): 20.18M ops/s - **C2/C3 Ring**: 21.15M ops/s (**+4.8%** improvement) ✅ - **C2/C3/C5 Ring**: 21.18M ops/s (**+5.0%** total improvement) ✅ - **Analysis**: - C2/C3 provide most of the gain (small sizes are hottest) - C5 addition provides marginal benefit (+0.03M ops/s) - Implementation complete and stable - **Files Modified**: - `core/front/tiny_ring_cache.h/c` - Ring buffer implementation - `core/tiny_alloc_fast.inc.h` - Alloc path integration - `core/tiny_free_fast_v2.inc.h` - Free path integration (line 154-160) --- **Phase 21-1-D: Ring Cache Default ON (2025-11-16)** 🚀 - **Goal**: Enable Ring Cache by default for production use (remove ENV gating) - **Implementation**: 1-line change in `core/front/tiny_ring_cache.h:72` - Changed logic: `g_enable = (e && *e == '0') ? 0 : 1; // DEFAULT: ON` - ENV=0 disables, ENV unset or ENV=1 enables - **Results** (`bench_random_mixed_hakmem 500K, 256B workload, 3-run average`): - **Ring ON** (default): **20.31M ops/s** (baseline) - **Ring OFF** (ENV=0): 19.30M ops/s - **Improvement**: **+5.2%** (+1.01M ops/s) ✅ - **Impact**: Ring Cache now active in all builds without manual ENV configuration --- **Performance Bottleneck Analysis (Task-sensei Report, 2025-11-16)** 🔍 **Root Cause: Cache Misses (6.6x worse than System malloc)** - **L1 D-cache miss rate**: HAKMEM 5.15% vs System 0.78% → **6.6x higher** - **IPC (instructions/cycle)**: HAKMEM 0.52 vs System 1.43 → **2.75x worse** - **Branch miss rate**: HAKMEM 11.86% vs System 4.77% → **2.5x higher** - **Per-operation cost**: HAKMEM **8-10 cache misses** vs System **2-3 cache misses** **Problem: 4-5 Layer Frontend Cascade** ``` Random Mixed allocation flow: Ring (L0) miss → FastCache (L1) miss → SFC (L2) miss → TLS SLL (L3) miss → SuperSlab refill (L4) = 8-10 cache misses per allocation (each layer = 2 misses: head + next pointer) ``` **System malloc tcache: 2-3 cache misses (single-layer array-based bins)** **Improvement Roadmap** (Target: 48-77M ops/s, System比 53-86%): 1. **P1 (Done)**: Ring Cache default ON → **+5.2%** (20.3M ops/s) ✅ 2. **P2 (Next)**: Unified Frontend Cache (flatten 4-5 layers → 1 layer) → **+50-100%** (30-40M expected) 3. **P3**: Adaptive refill optimization → **+20-30%** 4. **P4**: Branchless dispatch table → **+10-15%** 5. **P5**: Metadata locality optimization → **+15-20%** **Conservative Target**: 48M ops/s (+136% vs current, 53% of System) **Optimistic Target**: 77M ops/s (+279% vs current, 86% of System) --- **Phase 22: Lazy Per-Class Initialization (2025-11-16)** 🚀 - **Goal**: Cold-start page faultを削減 (ChatGPT分析: `hak_tiny_init()` → 94.94% of page faults) - **Strategy**: Eager init (全8クラス初期化) → Lazy init (使用クラスのみ初期化) - **Results** (`bench_random_mixed_hakmem 500K, 256B workload`): - **Cold-start**: 18.1M ops/s (Phase 21-1: 16.2M) → **+12% improvement** ✅ - **Steady-state**: 25.5M ops/s (Phase 21-1: 26.1M) → -2.3% (誤差範囲) - **Key Achievement**: `hak_tiny_init.part.0` 完全削除、未使用クラスのpage touchを回避 - **Remaining Bottleneck**: SuperSlab allocation時の`memset` page fault (42.40%) --- **📊 PERFORMANCE MAP (2025-11-16) - 全体性能俯瞰** 🗺️ ベンチマーク自動化スクリプト: `scripts/bench_performance_map.sh` 最新結果: `bench_results/performance_map/20251116_095827/` ### 🎯 固定サイズ (16-1024B) - Tiny層の現実 | Size | System | HAKMEM | Ratio | Status | |------|--------|--------|-------|--------| | 16B | 118.6M | 50.0M | 42.2% | ❌ Slow | | 32B | 103.3M | 49.3M | 47.7% | ❌ Slow | | 64B | 104.3M | 49.2M | 47.1% | ❌ Slow | | **128B** | **74.0M** | **51.8M** | **70.0%** | **⚠️ Gap** ✨ | | 256B | 115.7M | 36.2M | 31.3% | ❌ Slow | | 512B | 103.5M | 41.5M | 40.1% | ❌ Slow | | 1024B| 96.0M | 47.8M | 49.8% | ❌ Slow | **発見**: - **128Bのみ 70%** (唯一Gap範囲) - 他は全て50%未満 - **256Bが最悪 31.3%** - Phase 22で18.1M → 36.2Mに改善したが、systemの1/3に留まる - **小サイズ (16-64B) 42-47%** - UltraHot経由でも system の半分 ### 🌀 Random Mixed (128B-1KB) | Allocator | ops/s | vs System | |-----------|--------|-----------| | System | 90.2M | 100% (baseline) | | **Mimalloc** | **117.5M** | **130%** 🏆 (systemより速い!) | | **HAKMEM** | **21.1M** | **23.4%** ❌ (mimallocの1/5.5) | **衝撃的発見**: - Mimallocは system より 30%速い - HAKMEMは mimalloc の **1/5.5** - 巨大なギャップ ### 💥 CRITICAL ISSUES - Mid-Large / MT層が完全破壊 **Mid-Large MT (8-32KB)**: ❌ **CRASHED** (コアダンプ) - **原因**: `hkm_ace_alloc` が 33KB allocation で NULL返却 - **結果**: `free(): invalid pointer` → クラッシュ - **Mimalloc**: 40.2M ops/s (system の 449%!) - **HAKMEM**: 0 ops/s (動作不能) **VM Mixed**: ❌ **CRASHED** (コアダンプ) - System: 957K ops/s - HAKMEM: 0 ops/s **Larson (MT churn)**: ❌ **SEGV** - System: 3.4M ops/s - Mimalloc: 3.4M ops/s - HAKMEM: 0 ops/s --- **🔧 Mid-Large Crash FIX (2025-11-16)** ✅ **Root Cause (ChatGPT分析)**: - `classify_ptr()` が AllocHeader (Mid/Large mmap allocations) をチェックしていない - Free wrapper が `PTR_KIND_MID_LARGE` ケースを処理していない - 結果: Mid-Large ポインタが `PTR_KIND_UNKNOWN` → `__libc_free()` → `free(): invalid pointer` **修正内容**: 1. **`classify_ptr()` に AllocHeader チェック追加** (`core/box/front_gate_classifier.c:256-271`) - `hak_header_from_user()` + `hak_header_validate()` で HAKMEM_MAGIC 確認 - `ALLOC_METHOD_MMAP/POOL/L25_POOL` → `PTR_KIND_MID_LARGE` 返却 2. **Free wrapper に `PTR_KIND_MID_LARGE` ケース追加** (`core/box/hak_wrappers.inc.h:181`) - `is_hakmem_owned = 1` で HAKMEM 管轄として処理 **修正結果**: - **Mid-Large MT (8-32KB)**: 0 → **10.5M ops/s** (System 8.7M = **120%**) 🏆 - **VM Mixed**: 0 → **285K ops/s** (System 939K = 30.4%) - ✅ クラッシュ完全解消、Mid-Large で system malloc を **20% 上回る** **残存課題**: - ❌ **random_mixed**: SEGV (AllocHeader読み込みでページ境界越え) - ❌ **Larson**: SEGV継続 (Tiny 8-128B 領域、別原因) --- **🔧 random_mixed Crash FIX (2025-11-16)** ✅ **Root Cause**: - Mid-Large fix で追加した `classify_ptr()` の AllocHeader check が unsafe - AllocHeader = 40 bytes → `ptr - 40` がページ境界越えると SEGV - 例: `ptr = 0x7ffff6a00000` (page-aligned) → header at `0x7ffff69fffd8` (別ページ、unmapped) **修正内容** (`core/box/front_gate_classifier.c:263-266`): ```c // Safety check: Need at least HEADER_SIZE (40 bytes) before ptr uintptr_t offset_in_page_for_hdr = (uintptr_t)ptr & 0xFFF; if (offset_in_page_for_hdr >= HEADER_SIZE) { // Safe to read AllocHeader (won't cross page boundary) AllocHeader* hdr = hak_header_from_user(ptr); ... } ``` **修正結果**: - **random_mixed**: SEGV → **1.92M ops/s** ✅ - ✅ Single-thread workloads 完全修復 --- **🔧 Larson MT Crash FIX (2025-11-16)** ✅ **2-Layer Problem Structure**: **Layer 1: Cross-thread Free (TLS SLL Corruption)** - **Root Cause**: Block allocated by Thread A, freed by Thread B → pushed to B's TLS SLL - B allocates the block → metadata still points to A's SuperSlab → corruption - Poison values (0xbada55bada55bada) in TLS SLL → SEGV in `tiny_alloc_fast()` - **Fix** (`core/tiny_free_fast_v2.inc.h:176-205`): - Made cross-thread check **ALWAYS ON** (removed ENV gating) - Check `owner_tid_low` on every free, route cross-thread to remote queue via `tiny_free_remote_box()` - **Status**: ✅ **FIXED** - TLS SLL corruption eliminated **Layer 2: SP Metadata Capacity Limit** - **Root Cause**: `[SP_META_CAPACITY_ERROR] Exceeded MAX_SS_METADATA_ENTRIES=2048` - Larson rapid churn workload → 2048+ SuperSlabs → registry exhaustion → hang - **Fix** (`core/hakmem_shared_pool.h:122-126`): - Increased `MAX_SS_METADATA_ENTRIES` from 2048 → **8192** (4x capacity) - **Status**: ✅ **FIXED** - Larson completes successfully **Results** (10 seconds, 4 threads): - **Before**: 4.2TB virtual memory, 65,531 mappings, indefinite hang (kill -9 required) - **After**: 6.7GB virtual (-99.84%), 424MB RSS, completes in 10-18 seconds - **Throughput**: 7,387-8,499 ops/s (0.014% of system malloc 60.6M) **Layer 3: Performance Optimization (IN PROGRESS)** - Cross-thread check adds SuperSlab lookup on every free (20-50 cycles overhead) - **Drain Interval Tuning** (2025-11-16): - Baseline (drain=2048): 7,663 ops/s - Moderate (drain=1024): **8,514 ops/s** (+11.1%) ✅ - Aggressive (drain=512): Core dump ❌ (too aggressive, causes crash) - **Recommendation**: `export HAKMEM_TINY_SLL_DRAIN_INTERVAL=1024` for stable +11% gain - **Remaining Work**: LRU policy tuning (MAX_CACHED, MAX_MEMORY_MB, TTL_SEC) - Goal: Improve from 0.014% → 80% of system malloc (currently 0.015% with drain=1024) --- ### 📈 Summary (Performance Map 2025-11-16 17:15) **修正後の全体結果**: - ✅ Competitive (≥80%): **0/10 benchmarks** (0%) - ⚠️ Gap (50-80%): **1/10 benchmarks** (10%) ← 64B固定のみ 53.6% - ❌ Slow (<50%): **9/10 benchmarks** (90%) **主要ベンチマーク**: 1. **Fixed-size (16-1024B)**: 38.5-53.6% of system (64B が最良) 2. **Random Mixed (128-1KB)**: **19.4M ops/s** (24.0% of system) 3. **Mid-Large MT (8-32KB)**: **891K ops/s** (12.1% of system, crash 修正済み ✅) 4. **VM Mixed**: **275K ops/s** (30.7% of system, crash 修正済み ✅) 5. **Larson (MT churn)**: **7.4-8.5K ops/s** (0.014% of system, crash 修正済み ✅, 性能最適化は Layer 3 で対応予定) **優先課題 (2025-11-16 更新)**: 1. ✅ **完了**: Mid-Large crash 修復 (classify_ptr + AllocHeader check) 2. ✅ **完了**: VM Mixed crash 修復 (Mid-Large fix で解消) 3. ✅ **完了**: random_mixed crash 修復 (page boundary check) 4. 🔴 **P0**: Larson SP metadata limit 拡大 (2048 → 4096-8192) 5. 🟡 **P1**: Fixed-size 性能改善 (38-53% → 目標 80%+) 6. 🟡 **P1**: Random Mixed 性能改善 (24% → 目標 80%+) 7. 🟡 **P1**: Mid-Large MT 性能改善 (12% → 目標 80%+, mimalloc 449%が参考値) `bench_fixed_size_hakmem` / `bench_fixed_size_system`(workset=128, 500K iterations 相当) | Size | HAKMEM (Phase 15) | System malloc | 比率 | |--------|-------------------|---------------|----------| | 128B | ~16.6M ops/s | ~90M ops/s | ~18.5% | | 256B | ~16.2M ops/s | ~89.6M ops/s | ~18.1% | | 512B | ~15.0M ops/s | ~90M ops/s | ~16.6% | | 1024B | ~15.1M ops/s | ~90M ops/s | ~16.8% | 状態: - クラッシュは完全解消(workset=64/128 で長尺 500K iter も安定)。 - Tiny UltraHot + 学習層 + ExternalGuard の組み合わせは「正しさ」は OK。 - 性能は system の ~16–18% レベル(約 5–6× 遅い)→ まだ大きな伸びしろあり。 ### 2.2 C2/C3 UltraHot 専用ベンチ 固定サイズ(100K iterations, workset=128) | Size | Baseline (UltraHot OFF) | UltraHot ON | 改善率 | Hit Rate | |------|-------------------------|-------------|-------------|---------| | 16B | ~40.4M ops/s | ~55.0M | +36.2% 🚀 | ≈100% | | 32B | ~43.5M ops/s | ~50.6M | +16.3% 🚀 | ≈100% | Random Mixed 256B: - Baseline: ~8.96M ops/s - UltraHot ON: ~8.81M ops/s(-1.6%、誤差〜軽微退化) - 理由: C2/C3 が全体の 1–2% のみ → UltraHot のメリットが平均に薄まる。 結論: - C2/C3 UltraHot は **ターゲットクラスに対しては実用級の Box**。 - 他ワークロードでは「ほぼ影響なし(わずかな分岐オーバーヘッドのみ)」の範囲に収まっている。 --- ## 3. Phase 15: ExternalGuard / Domain 分離の成果 ### 3.1 以前の問題 - free ラッパ(`core/box/hak_wrappers.inc.h`)が: - HAKMEM 所有かチェックせず、すべての `free(ptr)` を `hak_free_at(ptr, …)` に投げていた。 - その結果: - ベンチ内部 `slots`(`calloc(256, sizeof(void*))` の 2KB など)も CoreAlloc に流入。 - `classify_ptr` → UNKNOWN → ExternalGuard → mincore → 「解放せず leak」と判定。 - ベンチ観測: - 約 0.84% の leak(BenchMeta がどんどん漏れる)。 - `mincore` が Tiny ベンチ CPU の ~13% を消費。 ### 3.2 修正内容(Phase 15) - free ラッパ側: - 軽量なドメインチェックを追加: - Tiny/Pool 用の header magic を安全に読んで、HAKMEM 所有の可能性があるものだけ `hak_free_at` へ。 - そうでない(BenchMeta/外部)ポインタは `__libc_free` へ。 - ExternalGuard: - UNKNOWN ポインタを「解放しない(leak)」方針に明示的変更。 - デバッグ時のみ `HAKMEM_EXTERNAL_GUARD_LOG=1` で原因特定用ログを出す。 ### 3.3 結果 - Leak 率: - 100K iter: 840 leaks → 0.84% - 500K iter: ~4200 leaks → 0.84% - ほぼ全部が BenchMeta / 外部ポインタであり、CoreAlloc 側の漏れではないと確認。 - 性能: - 256B 固定: - Before: 15.9M ops/s - After: 16.2M ops/s(+1.9%)→ domain check オーバーヘッドは軽微、むしろ微増。 - 安定性: - 全サイズ(128/256/512/1024B)で 500K iter 完走(クラッシュなし)。 - ExternalGuard 経由の「危ない free」は leak に封じ込められた。 **要点:** Box 境界違反(BenchMeta→CoreAlloc 流入)はほぼ完全に解消。 ベンチでの mincore / ExternalGuard コストも許容範囲になった。 --- ## 4. Phase 16: Dynamic Tiny/Mid Boundary A/B Testing(2025-11-16完了) ### 4.1 実装内容 ENV変数でTiny/Mid境界を動的調整可能にする機能を追加: - `HAKMEM_TINY_MAX_CLASS=7` (デフォルト): Tiny が 0-1023B を担当 - `HAKMEM_TINY_MAX_CLASS=5` (実験用): Tiny が 0-255B のみ担当 実装ファイル: - `hakmem_tiny.h/c`: `tiny_get_max_size()` - ENV読取とクラス→サイズマッピング - `hakmem_mid_mt.h/c`: `mid_get_min_size()` - 動的境界調整(サイズギャップ防止) - `hak_alloc_api.inc.h`: 静的TINY_MAX_SIZEを動的呼び出しに変更 ### 4.2 A/B Benchmark Results | Size | Config A (C0-C7) | Config B (C0-C5) | 変化率 | |------|------------------|------------------|--------| | 128B | 6.34M ops/s | 1.38M ops/s | **-78%** ❌ | | 256B | 6.34M ops/s | 1.36M ops/s | **-79%** ❌ | | 512B | 5.55M ops/s | 1.33M ops/s | **-76%** ❌ | | 1024B | 5.91M ops/s | 1.37M ops/s | **-77%** ❌ | ### 4.3 発見と結論 ✅ **成功**: サイズギャップ修正完了(OOMクラッシュなし) ❌ **失敗**: Tiny カバレッジ削減で大幅な性能劣化 (-76% ~ -79%) ⚠️ **根本原因**: Mid の粗いサイズクラス (8KB/16KB/32KB) が小サイズで非効率 - Mid は 8KB ページ単位の設計 → 256B-1KB を投げると 8KB ページをほぼ数ブロックのために確保 - ページ fault・TLB・メタデータコストが相対的に巨大 - Tiny は slab + freelist で高密度 → 同じサイズでも桁違いに効率的 **教訓(ChatGPT先生分析)**: 1. Mid 箱の前提が「8KB〜用」になっている - 256B/512B/1024B では 8KB ページをほぼ1〜数個のブロックのために確保 → 非効率 2. パス長も Mid の方が長い(PoolTLS / mid registry / page 管理) 3. 「Tiny を削って Mid に任せれば軽くなる」という仮説は、現行の "8KB〜前提の Mid 設計" では成り立たない **推奨**: **デフォルト HAKMEM_TINY_MAX_CLASS=7 (C0-C7) を維持** --- ## 5. Phase 17: Small-Mid Allocator Box - 実験完了 ✅(2025-11-16) ### 5.1 目標と動機 **問題**: Tiny C5-C7 (256B/512B/1KB) が ~6M ops/s → system malloc の ~6.7% レベル **仮説**: 専用層を作れば 2-4x 改善可能 **結果**: ❌ **仮説は誤り** - 性能改善なし(±0-1%) ### 5.2 Phase 17-1: TLS Frontend Cache(Tiny delegation) **実装**: - TLS freelist(256B/512B/1KB、容量32/24/16) - Backend: Tiny C5/C6/C7に委譲、Header変換(0xa0 → 0xb0) - Auto-adjust: Small-Mid ON時にTinyをC0-C5に自動制限 **結果**: | Size | OFF | ON | 変化率 | |------|-----|-----|--------| | 256B | 5.87M | 6.06M | **+3.3%** | | 512B | 6.02M | 5.91M | **-1.9%** | | 1024B | 5.58M | 5.54M | **-0.6%** | | **平均** | 5.82M | 5.84M | **+0.3%** | **教訓**: Delegation overhead = TLS savings → 正味利益ゼロ ### 5.3 Phase 17-2: Dedicated SuperSlab Backend **実装**: - Small-Mid専用SuperSlab pool(1MB、16 slabs/SS) - Batch refill(8-16 blocks/refill) - 直接0xb0 header書き込み(Tiny delegationなし) **結果**: | Size | OFF | ON | 変化率 | |------|-----|-----|--------| | 256B | 6.08M | 5.84M | **-4.1%** ⚠️ | | 512B | 5.79M | 5.86M | **+1.2%** | | 1024B | 5.42M | 5.44M | **+0.4%** | | **平均** | 5.76M | 5.71M | **-0.9%** | **Phase 17-1比較**: Phase 17-2の方が悪化(-3.6% on 256B) ### 5.4 根本原因分析(ChatGPT先生 + perf profiling) **発見**: **70% page fault** が支配的 🔥 **Perf分析**: - `asm_exc_page_fault`: 70% CPU時間 - 実際のallocation logic(TLS/refill): 30% のみ - **結論**: Frontend実装は成功、Backendが重すぎる **なぜpage faultが多いか**: ``` Small-Mid: alloc → TLS miss → refill → SuperSlab新規確保 → mmap(1MB) → page fault 発生 → 70%のCPU消費 Tiny: alloc → TLS miss → refill → 既存warm SuperSlab使用 → page faultなし → 高速 ``` **Small-Mid問題**: 1. 新しいSuperSlabを頻繁に確保(workloadが短いため) 2. Warm SuperSlabの再利用なし(usedカウンタ減らない) 3. Batch refillのメリットよりmmap/page faultコストが大きい ### 5.5 Phase 17の結論と教訓 ❌ **Small-Mid専用層戦略は失敗**: - Phase 17-1(Frontend only): +0.3% - Phase 17-2(Dedicated backend): -0.9% - 目標(2-4x改善): **未達成**(-50-67%不足) ✅ **重要な発見**: 1. **Frontend(TLS/batch refill)設計はOK** - 30%のみの負荷 2. **70% page fault = SuperSlab層の問題** 3. **Tiny (6.08M) は既に十分速い** - これを超えるのは困難 4. **層の分離では性能は上がらない** - Backend最適化が必要 ✅ **実装の価値**: - ENV=0でゼロオーバーヘッド(branch predictor学習) - 実験記録として価値あり("なぜ専用層が効果なかったか"の証拠) - Tiny最適化の邪魔にならない(完全分離アーキテクチャ) ### 5.6 次のステップ: SuperSlab Reuse(Phase 18候補) **ChatGPT提案**: Tiny SuperSlabの最適化(Small-Mid専用層ではなく) **Box SS-Reuse(SuperSlab slab再利用箱)**: - **目標**: 70% page fault → 5-10%に削減 - **戦略**: 1. meta->freelistを優先使用(現在はbump onlyで再利用なし) 2. slabがemptyになったらshared_poolに返却 3. 同じSuperSlab内で長く回す(新規mmap削減) - **効果**: page fault大幅削減 → 2-4x改善期待 - **実装場所**: `core/hakmem_tiny_superslab.c`(Tiny用、Small-Midではない) **Box SS-Prewarm(事前温め箱)**: - クラスごとにSuperSlabを事前確保(Phase 11実績: +6.4%) - page faultをbenchmark開始時に集中 - **課題**: benchmark専用、実運用では無駄 **推奨**: Box SS-Reuse優先(実運用価値あり、根本解決) --- ## 6. 未達成の目標・残課題(次フェーズ候補) ### 6.1 Tiny 性能ギャップ(System の ~18% 止まり) 現状: - System malloc が ~90M ops/s レベルのところ、 - HAKMEM は 128〜1024B 固定で ~15–16M ops/s(約 18%)。 原因の切り分け(これまでの調査から): - Front(UltraHot/TinyHeapV2/TLS SLL)のパス長はかなり短縮済み。 - L1 dcache miss / instructions / branches は Phase 14 で大幅削減済みだが、 - まだ Tiny が 0–1023B を全部抱えており、 - 特に 512/1024B が Superslab/Pool 側のメタ負荷に効いている可能性。 候補: - **Phase 17 で実装中!** Small-Mid Box(256B〜4KB 専用箱)を設計し、Tiny/Mid の間を分離する。 - 詳細は § 5. Phase 17 を参照 ### 6.2 UltraHot/TinyHeapV2 の拡張 or 整理 - C2/C3 UltraHot は成功(16/32B 用)。 - C4/C5 まで拡張した試み(Phase 14-B)は: - Fixed-size では改善あり。 - Random Mixed で shared_pool_acquire_slab() が 47.5% まで膨らみ、大退化。 - 原因: Superslab/TLS 在庫のバランスを壊す「窃取カスケード」。 方針: - UltraHot は **C2/C3 専用 Box** に戻す(C4/C5 は一旦対象外にする)。 - もし C4/C5 を最適化したいなら、SmallMid Box の中で別設計する。 ### 6.3 ExternalGuard の統計と自動アラート - 現在: - `HAKMEM_EXTERNAL_GUARD_STATS=1` で統計を手動出力。 - 100+ 回呼ばれたら WARNING を出すのみ。 - 構想: - 「ExternalGuard 呼び出しが一定閾値を超えたら、自動で簡易レポートを吐く」Box を追加。 - 例: Top N 呼び出し元アドレス、サイズ帯、mincore 結果 など。 --- ## 7. Claude Code 君向け TODO ### 7.1 Phase 17: Small-Mid Allocator Box ✅ 完了(2025-11-16) **Phase 17-1**: TLS Frontend Cache - ✅ 実装完了(TLS freelist + Tiny delegation) - ✅ A/B テスト: ±0.3%(性能改善なし) - ✅ 教訓: Delegation overhead = TLS savings **Phase 17-2**: Dedicated SuperSlab Backend - ✅ 実装完了(専用SuperSlab pool + batch refill) - ✅ A/B テスト: -0.9%(Phase 17-1より悪化) - ✅ 根本原因: 70% page fault(mmap/SuperSlab確保が重い) **結論**: Small-Mid専用層は性能改善なし(±0-1%)、Tiny最適化が必要 ### 7.2 Phase 18 候補: SuperSlab Reuse(Tiny最適化) **Box SS-Reuse(最優先)**: 1. meta->freelist優先使用(現状: bump only) 2. slab empty検出→shared_pool返却 3. 同じSuperSlab内で長く回す(page fault削減) 4. 目標: 70% page fault → 5-10%、性能 2-4x改善 **Box SS-Prewarm(次優先)**: 1. クラスごとSuperSlab事前確保 2. page faultをbenchmark開始時に集中 3. Phase 11実績: +6.4%(参考値) **Box SS-HotHint(長期)**: 1. クラス別ホットSuperSlab管理 2. locality最適化(cache効率) 3. SS-Reuseとの統合 ### 7.3 その他タスク 1. ✅ **Phase 16/17 結果分析** - CURRENT_TASK.md記録完了 2. **C2/C3 UltraHot コード掃除** - C4/C5関連を別Box化 3. **ExternalGuard 統計自動化** - 閾値超過時レポート --- ## 8. Phase 17 実装ログ(完了) ### 2025-11-16 - ✅ **Phase 17-1完了**: TLS Frontend + Tiny delegation - 実装: `hakmem_smallmid.h/c`, auto-adjust, routing修正 - A/B結果: +0.3%(性能改善なし) - 教訓: Delegation overhead = TLS savings - ✅ **Phase 17-2完了**: Dedicated SuperSlab backend - 実装: `hakmem_smallmid_superslab.h/c`, batch refill, 0xb0 header - A/B結果: -0.9%(Phase 17-1より悪化) - 根本原因: 70% page fault(ChatGPT + perf分析) - ✅ **重要な発見**: - Frontend(TLS/batch refill): OK(30%のみ) - Backend(SuperSlab確保): ボトルネック(70% page fault) - 専用層では性能上がらない → **Tiny SuperSlab最適化が必要** - ✅ **CURRENT_TASK.md更新**: Phase 17結果 + Phase 18計画 - 🎯 **次**: Phase 18 Box SS-Reuse実装(Tiny SuperSlab最適化) --- ## 9. Phase 19 実装ログ(完了) 🎉 ### 2025-11-16 - ✅ **Phase 19-1完了**: Box FrontMetrics(観測) - 実装: `core/box/front_metrics_box.h/c`、全層にヒット率計測追加 - ENV: `HAKMEM_TINY_FRONT_METRICS=1`, `HAKMEM_TINY_FRONT_DUMP=1` - 結果: CSV形式で per-class ヒット率レポート生成 - ✅ **Phase 19-2完了**: ベンチマークとヒット率分析 - ワークロード: Random Mixed 16-1040B、50万イテレーション - **重要な発見**: - **HeapV2**: 88-99% ヒット率(主力として機能)✅ - **UltraHot**: 0.2-11.7% ヒット率(ほぼ素通り)⚠️ - FC/SFC: 無効化済み(0%) - TLS SLL: fallback として 0.7-2.7% のみ - ✅ **Phase 19-3完了**: Box FrontPrune(診断) - 実装: ENV切り替えで層を個別ON/OFF可能 - ENV: `HAKMEM_TINY_FRONT_ENABLE_ULTRAHOT=1`(デフォルトOFF) - ENV: `HAKMEM_TINY_FRONT_DISABLE_HEAPV2=1`(デフォルトON) - ✅ **Phase 19-4完了**: A/Bテストと最適化 - **テスト結果**: | 設定 | 性能 | vs Baseline | C2/C3 ヒット率 | |------|------|-------------|----------------| | Baseline(両方ON) | 10.1M ops/s | - | UH=11.7%, HV2=88.3% | | **HeapV2のみ** | **11.4M ops/s** | **+12.9%** ⭐ | HV2=99.3%, SLL=0.7% | | UltraHotのみ | 6.6M ops/s | -34.4% ❌ | UH=96.4% (C2), SLL=94.2% (C3) | - **決定的結論**: - **UltraHot削除で性能向上** (+12.9%) - 理由: 分岐予測ミスコスト > UltraHotヒット率向上効果 - UltraHotチェック: 88.3%のケースで無駄な分岐 → CPU分岐予測器を混乱 - HeapV2単独の方が予測可能性が高い → 性能向上 - ✅ **デフォルト設定変更**: UltraHot デフォルトOFF - 本番推奨: UltraHot OFF(最速設定) - 研究用: `HAKMEM_TINY_FRONT_ENABLE_ULTRAHOT=1` で有効化可能 - コードは削除せず ENV切り替えで残す(研究・デバッグ用) - ✅ **Phase 19 成果**: - ChatGPT先生の「観測→診断→治療」戦略が完璧に機能 🎓 - 直感に反する発見(UltraHotが阻害要因)をデータで証明 - A/Bテストでリスクなし確認してから最適化実施 - 詳細: `PHASE19_FRONTEND_METRICS_FINDINGS.md`, `PHASE19_AB_TEST_RESULTS.md` --- ## 10. Phase 20 計画: Tiny ホットパス一本化 + BenchFast モード 🎯 ### 目標 - **性能目標**: 20-30M ops/s(system malloc の 25-35%) - **設計目標**: 「箱を崩さず」に達成(研究価値を保つ) ### Phase 20-1: HeapV2 を唯一の Tiny Front に(本命ホットパス一本化) **現状認識**: - C2/C3: HeapV2 が 88-99% を処理(本命) - UltraHot: 0.2-11.7% しか当たらず、分岐の邪魔(削ると +12.9%) - FC/SFC: 実質 OFF、TLS SLL は fallback のみ **実装方針**: 1. **HeapV2 を「唯一の front」として扱う**: - C2-C5: HeapV2 → fallback だけ TLS SLL - 他層(UltraHot, FC, SFC)はホットパスから完全に外し、実験用に退避 2. **HeapV2 の中身を徹底的に薄くする**: - size→class 再計算を全部やめて、「class_idx を渡すだけ」にする - 分岐を「classごとの専用関数」かテーブルジャンプにして 1-2 本に減らす - header 書き込み・TLS stack 操作・return までを「6-8 命令の直線」に近づける 3. **期待効果**: - 現在 11M ops/s → 目標 15-20M ops/s (+35-80% 改善) - 分岐削減 + 命令直線化 → CPU パイプライン効率向上 **ENV制御**: ```bash # HeapV2専用モード(Phase 20デフォルト) HAKMEM_TINY_FRONT_HEAPV2_ONLY=1 # UltraHot/FC/SFC完全バイパス # 旧動作(研究用) HAKMEM_TINY_FRONT_ENABLE_ULTRAHOT=1 # Phase 19設定 ``` --- ### Phase 20-2: BenchFast モードで安全コストを外す **現状認識**: - `hak_free_at` / `classify_ptr` / ExternalGuard / mincore など、 「LD_PRELOAD / 外部ライブラリから守る」層が、 ベンチでは「絶対に hakmem だけを使っている」前提の上に乗っている **実装方針**: 1. **ベンチ用完全信頼モード**(Box BenchFast): - alloc/free ともに: - header 1バイト で Tiny を即判定 - Pool/Mid/L25/ExternalGuard/registry を完全にバイパス - 変なポインタが来たら壊れていい(ベンチ用なので) 2. **ENV制御**: ```bash HAKMEM_BENCH_FAST_MODE=1 # 安全コスト全外し ``` 3. **目的**: - 「箱全部乗せ版」と「安全コスト全外し版」の差を測る - 「設計そのものの限界」と「安全・汎用性のコスト」の内訳を見る - mimalloc と同じくらい「危ないモード」で、どこまで近づけるかを研究 4. **期待効果**: - HeapV2専用モード: 15-20M ops/s - BenchFast追加: 25-30M ops/s (+65-100% vs 現状) - system malloc (90M ops/s) の 28-33% に到達 --- ### Phase 20-3: SuperSlab ホットセット チューニング **現状認識**: - SS-Reuse: 再利用率 98.8%、新規 mmap 1.2% → page fault は抑えられている - とはいえ perf ではまだ `asm_exc_page_fault` がでかく見える場面もある **実装方針**: 1. **Box SS-HotSet**(どのクラスが何枚をホットに持つか計測): - クラスごとの「ホット SuperSlab 数」を 1-2 枚に抑えるように class_hints をチューニング - precharge (`HAKMEM_TINY_SS_PRECHARGE_Cn`) を使って、「最初から 2 枚だけ温める」戦略を試す 2. **Box SS-Compact**(ホットセット圧縮): - 同じ SuperSlab に複数のホットクラスを詰め込む(Phase 12 の発展) - 例: C2/C3 を同じ SuperSlab に配置 → キャッシュ効率向上 3. **期待効果**: - page fault さらに削減 → +10-20% 性能向上 - 既存の SS-Reuse/Cache 設計を、「Tiny front が見ているサイズ帯に合わせて細かく調整」 --- ### Phase 20 実装順序 1. **Phase 20-1**: HeapV2 専用モード実装(優先度: 高) - 期待: +35-80% (11M → 15-20M ops/s) - 工数: 中(既存 HeapV2 をスリム化) 2. **Phase 20-2**: BenchFast モード実装(優先度: 中) - 期待: +65-100% (11M → 25-30M ops/s) - 工数: 中(安全層バイパス) 3. **Phase 20-3**: SS-HotSet チューニング(優先度: 低) - 期待: +10-20% 追加改善 - 工数: 小(パラメータ調整 + 計測箱追加) --- ### Phase 20 成功条件 - ✅ Tiny 固定サイズで 20-30M ops/s 達成(system の 25-35%) - ✅ 「箱を崩さず」達成(研究箱としての価値を保つ) - ✅ ENV切り替えで「安全モード」「ベンチモード」を選べる状態を維持 - ✅ 残りの差(system との 2.5-3x)は「kernel/page fault + mimalloc の極端な inlining」と言える根拠を固める --- ### Phase 20 後の展望 ここまで行けたら: - 「残りの差は kernel/page fault + mimalloc の極端な inlining・OS依存の差」だと自信を持って言える - hakmem の「研究箱」としての価値(構造をいじりやすい / 可視化しやすい)を保ったまま、 性能面でも「そこそこ実用に耐える」ラインに乗る - 学術論文・技術ブログでの発表材料が揃う --- ## 11. Phase 20-1 実装ログ: Box SS-HotPrewarm(TLS Cache 事前確保) ✅ ### 2025-11-16 #### 実装内容 - ✅ **Box SS-HotPrewarm 作成**: ENV制御の per-class TLS cache prewarm - 実装: `core/box/ss_hot_prewarm_box.h/c` - デフォルト targets: C2/C3=128, C4/C5=64(aggressive prewarm) - ENV制御: `HAKMEM_TINY_PREWARM_C2`, `_C3`, `_C4`, `_C5`, `_ALL` - ✅ **初期化統合**: `hak_init_impl()` から自動呼び出し - 384 ブロック事前確保(C2=128, C3=128, C4=64, C5=64) - `box_prewarm_tls()` API 使用(安全な carve-push) #### ベンチマーク結果(500K iterations, 256B random mixed) | 設定 | Page Faults | Throughput | vs Baseline | |------|-------------|------------|-------------| | **Baseline** (Prewarm OFF) | 10,399 | 15.7M ops/s | - | | **Phase 20-1** (Prewarm ON) | 10,342 | 16.2M ops/s | **+3.3%** ⭐ | - **Page fault 削減**: 0.55%(期待: 50-66% → 現実: ほぼなし) - **性能向上**: +3.3%(15.7M → 16.2M ops/s) #### 分析と結論 **❌ Page Fault 削減の失敗理由**: 1. **ユーザーページ由来が支配的**: ベンチマーク自体の初期化・データ構造確保による page fault が大半 2. **SuperSlab 事前確保の限界**: 384 ブロック程度の prewarm では、ベンチマーク全体の page fault (10K+) に対して微々たる影響しかない 3. **カーネル側のコスト**: `asm_exc_page_fault` はユーザー空間だけでは制御不可能 **✅ Cache Warming 効果**: 1. **TLS SLL 事前充填**: 初期の refill コスト削減 2. **CPU サイクル節約**: +3.3% の性能向上 3. **安定性向上**: 初期状態が warm → 最初のアロケーションから高速 #### 決定: 「軽い +3% 箱」として確定 - **prewarm は有効**: 384 ブロック確保(C2/C3=128, C4/C5=64)のまま残す - **これ以上の aggressive 化は不要**: RSS 消費増 vs page fault 削減効果が見合わない - **次フェーズへ**: BenchFast モードで「上限性能」を測定し、構造的限界を把握 #### 変更ファイル - `core/box/ss_hot_prewarm_box.h` - NEW - `core/box/ss_hot_prewarm_box.c` - NEW - `core/box/hak_core_init.inc.h` - prewarm 呼び出し追加 - `Makefile` - `ss_hot_prewarm_box.o` 追加 --- **Status**: Phase 20-1 完了 ✅ → **Phase 20-2 準備中** 🎯 **Next**: BenchFast モード実装(安全コスト全外し → 構造的上限測定) --- ## Phase 20-2: BenchFast Mode Implementation (2025-11-16) ✅ **Status**: ✅ **COMPLETE** - Recursion fixed via prealloc pool + init guard **Goal**: Measure HAKMEM's structural performance ceiling by removing ALL safety costs **Implementation**: Complete (core/box/bench_fast_box.{h,c}) ### Design Philosophy BenchFast mode bypasses all safety mechanisms to measure the theoretical maximum throughput: **Alloc path** (6-8 instructions): - size → class_idx → TLS SLL pop → write header → return USER pointer - Bypasses: classify_ptr, Pool/Mid routing, registry, refill logic **Free path** (3-5 instructions): - Read header → BASE pointer → TLS SLL push - Bypasses: registry lookup, mincore, ExternalGuard, capacity checks ### Implementation Details **Files Created**: - `core/box/bench_fast_box.h` - ENV-gated API with recursion guard - `core/box/bench_fast_box.c` - Ultra-minimal alloc/free + prealloc pool **Integration**: - `core/box/hak_wrappers.inc.h` - malloc()/free() wrappers with BenchFast bypass - `bench_random_mixed.c` - bench_fast_init() call before benchmark loop - `Makefile` - bench_fast_box.o added to all object lists **Activation**: ```bash export HAKMEM_BENCH_FAST_MODE=1 ./bench_fixed_size_hakmem 500000 256 128 ``` ### Recursion Fix: Prealloc Pool Strategy **Problem**: When TLS SLL is empty, bench_fast_alloc() → hak_alloc_at() → malloc() → infinite loop **Solution** (User's "C案"): 1. **Prealloc pool**: bench_fast_init() pre-allocates 50K blocks per class using normal path 2. **Init guard**: `bench_fast_init_in_progress` flag prevents BenchFast during init 3. **Pop-only alloc**: bench_fast_alloc() only pops from pool, NO REFILL **Key Fix** (User's contribution): ```c // core/box/bench_fast_box.h extern __thread int bench_fast_init_in_progress; // core/box/hak_wrappers.inc.h (malloc wrapper) if (__builtin_expect(!bench_fast_init_in_progress && bench_fast_enabled(), 0)) { return bench_fast_alloc(size); // Only activate AFTER init complete } ``` ### Performance Results (500K iterations, 256B fixed-size) | Mode | Throughput | vs Baseline | vs System | |------|------------|-------------|-----------| | **Baseline** (通常) | 54.4M ops/s | - | 53.3% | | **BenchFast** (安全コスト除去) | 56.9M ops/s | **+4.5%** | 55.7% | | **System malloc** | 102.1M ops/s | +87.6% | 100% | ### 🔍 Critical Discovery: Safety Costs Are NOT the Bottleneck **BenchFast で安全コストをすべて除去しても、わずか +4.5% しか改善しない!** **What this reveals**: - classify_ptr、Pool/Mid routing、registry、mincore、ExternalGuard → これらは**ボトルネックではない** - 本当のボトルネックは**構造的な部分**: - SuperSlab 設計(1 SS = 1 class 固定) - メタデータアクセスパターン(cache miss 多発) - TLS SLL 効率(pointer chasing overhead) - 877 SuperSlab 生成による巨大なメタデータフットプリント **System malloc との差**: - Baseline: 47.7M ops/s 遅い(-46.7%) - BenchFast でも 45.2M ops/s 遅い(-44.3%) - → 安全コスト除去しても差は **たった 2.5M ops/s しか縮まらない** ### Implications for Future Work **増分最適化の限界**: - Phase 9-11 で学んだ教訓を確認:症状の緩和では埋まらない - 安全コストは全体の 4.5% しか占めていない - 残り 95.5% は**構造的なボトルネック** **Phase 12 Shared SuperSlab Pool の重要性**: - 877 SuperSlab → 100-200 に削減 - メタデータフットプリント削減 → cache miss 削減 - 動的 slab 共有 → 使用効率向上 - 期待性能: 70-90M ops/s(System の 70-90%) ### Bottleneck Breakdown (推定) | コンポーネント | CPU 時間 | BenchFast で除去? | |---------------|----------|------------------| | SuperSlab metadata access | ~35% | ❌ 構造的 | | TLS SLL pointer chasing | ~25% | ❌ 構造的 | | Refill + carving | ~15% | ❌ 構造的 | | classify_ptr + registry | ~10% | ✅ 除去済み | | Pool/Mid routing | ~5% | ✅ 除去済み | | mincore + guards | ~5% | ✅ 除去済み | | その他 | ~5% | - | **結論**: 構造的ボトルネック(75%)>> 安全コスト(20%) **Next Steps**: - Phase 12: Shared SuperSlab Pool(本質的解決) - 877 SuperSlab → 100-200 に削減して cache miss を大幅削減 - 期待性能: 70-90M ops/s(System の 70-90%) **Phase 20 完了**: BenchFast モードで「安全コストは 4.5%」と証明 ✅ --- ## Phase 21: Hot Path Cache Optimization (HPCO) - 構造的ボトルネック攻略 🎯 **Status**: 🚧 **PLANNING** (ChatGPT先生のフィードバック反映済み) **Goal**: アクセスパターン最適化で 60% CPU(メタアクセス 35% + ポインタチェイス 25%)を直接攻撃 **Target**: 75-82M ops/s(System malloc の 73-80%) ### Phase 20-2 で判明した構造的ボトルネック **BenchFast の結論**: - 安全コスト(classify_ptr/Pool routing/registry/mincore/guards)= **4.5%** しかない - 残り 45M ops/s の差 = **箱の積み方そのもの** **支配的ボトルネック** (60% CPU): ``` メタアクセス: ~35% (SuperSlab/TinySlabMeta の複数フィールド読み書き) ポインタチェイス: ~25% (TLS SLL の next ポインタたどり) carve/refill: ~15% (batch carving + metadata updates) ``` **1 回の alloc/free で発生すること**: - 何段も構造体を跨ぐ(TLS → SuperSlab → SlabMeta → freelist) - ポインタを何回もたどる(SLL の next チェイン) - メタデータを何フィールドも触る(used/capacity/carved/freelist/...) ### Phase 21 戦略(ChatGPT先生フィードバック反映) #### Phase 21-1: Array-Based TLS Cache (C2/C3) 🔴 最優先 **狙い**: TLS SLL のポインタチェイス削減 → +15-20% **現状の問題**: ```c // TLS SLL (linked list) - 3 メモリアクセス、うち 1 回は cache miss void* ptr = g_tls_sll_head[class_idx]; // 1. ヘッド読み込み void* next = *(void**)ptr; // 2. next ポインタ読み込み (cache miss!) g_tls_sll_head[class_idx] = next; // 3. ヘッド更新 ``` **解決策: Ring Buffer**: ```c // Box 21-1: Array-based hot cache (C2/C3 only) typedef struct { void* slots[128]; // 初期サイズ 128(ENV で A/B: 64/128/256) uint16_t head; // pop index uint16_t tail; // push index } TlsRingCache; static __thread TlsRingCache g_hot_cache_c2; static __thread TlsRingCache g_hot_cache_c3; // Ultra-fast alloc (1-2 命令) void* ptr = g_hot_cache_c2.slots[g_hot_cache_c2.head++ & 0x7F]; // ring wrap ``` **階層化** (ChatGPT先生フィードバック): ``` Ring → SLL → SuperSlab ↑ ↑ ↑ L0 L1 L2 - alloc: Ring → 空なら SLL → 空なら SuperSlab - free: Ring → 満杯なら SLL - drain: SLL → Ring に昇格(一方向) ``` **効果**: - ポインタチェイス: 1 回 → **0 回** - メモリアクセス: 3 → **2 回** - cache locality: 配列は連続メモリ - **期待: +15-20%** (54.4M → 62-65M ops/s) **ENV 変数**: ```bash HAKMEM_TINY_HOT_RING_C2=128 # C2 Ring サイズ (default: 128) HAKMEM_TINY_HOT_RING_C3=128 # C3 Ring サイズ (default: 128) HAKMEM_TINY_HOT_RING_ENABLE=1 # Ring cache 有効化 ``` **実装ポイント** (ChatGPT先生): - Ring サイズは 64/128/256 で A/B テスト - C0/C1/C4/C5/C6/C7 は SLL のまま(使用頻度低い) - drain 時: SLL → Ring への昇格(一方向) - Ring が空 → SLL fallback → SuperSlab refill #### Phase 21-2: Hot Slab Direct Index 🟡 中優先度 **狙い**: SuperSlab → slab ループ削減 → +10-15% **現状の問題**: ```c // 毎回 32 slab をスキャン SuperSlab* ss = g_tls_slabs[class_idx].ss; for (int i = 0; i < 32; i++) { // ← ループ! TinySlabMeta* meta = &ss->slabs[i]; if (meta->freelist != NULL) { ... } } ``` **解決策: Hot Slab Cache**: ```c // Box 21-2: Direct index to hot slab static __thread TinySlabMeta* g_hot_slab[TINY_NUM_CLASSES]; void refill_from_hot_slab(int class_idx) { TinySlabMeta* hot = g_hot_slab[class_idx]; // Hot slab が空なら更新 if (!hot || hot->freelist == NULL) { hot = find_nonempty_slab(class_idx); // 1回だけ探索 g_hot_slab[class_idx] = hot; // cache! } pop_batch_from_freelist(hot, ...); // no loop! } ``` **効果**: - SuperSlab → slab ループ: 削除 - メタアクセス: 32 回 → **1 回** - **期待: +10-15%** (62-65M → 70-75M ops/s) **実装ポイント** (ChatGPT先生): - Hot slab が EMPTY → find_nonempty_slab で差し替え - free 時: hot slab に返す or freelist に戻す(ポリシー決める) - shared_pool / SS-Reuse との整合性確保 #### Phase 21-3: Minimal Meta Access (C2/C3) 🟢 低優先度 **狙い**: 触るフィールド削減 → +5-10% **現状の問題**: ```c // 1 alloc/free で 4-5 フィールド触る typedef struct { uint16_t used; // ✅ 必須 uint16_t capacity; // ❌ compile-time 定数化できる uint16_t carved; // ❌ C2/C3 では使わない void* freelist; // ✅ 必須 } TinySlabMeta; ``` **解決策: アクセスパターン限定** (ChatGPT先生): ```c // struct を分けなくてもOK(型分岐を避ける) // C2/C3 コードパスで触るのを used/freelist だけに限定 #define C2_CAPACITY 64 // compile-time 定数 static inline int c2_can_alloc(TinySlabMeta* meta) { return meta->used < C2_CAPACITY; // capacity フィールド不要! } ``` **効果**: - 触るフィールド: 4-5 → **2 個** (used/freelist のみ) - cache line 消費: 削減 - **期待: +5-10%** (70-75M → 75-82M ops/s) **実装ポイント** (ChatGPT先生): - struct 分離は後回し(型分岐コスト vs 効果のトレードオフ) - アクセスパターン限定だけでも cache 効果あり - Phase 21-1/2 の結果を見てから判断 ### Phase 21 実装順序 ``` Phase 21-1 (Array-based TLS Cache C2/C3): ↓ +15-20% → 62-65M ops/s Phase 21-2 (Hot Slab Direct Index): ↓ +10-15% → 70-75M ops/s Phase 21-3 (Minimal Meta Access): ↓ +5-10% → 75-82M ops/s ↓ 🎯 Target: System malloc の 73-80% ``` **Phase 12 (SuperSlab 共有) は後回し**: - Phase 21 で 80M ops/s 到達後、残り 20M ops/s を Phase 12 で詰める ### ChatGPT先生フィードバック(重要) 1. **Box 21-1 (Ring cache)**: ✅ perf 的にドンピシャ - Ring → SLL → SuperSlab の階層を明確に - Ring サイズは 128/64 から ENV で A/B - drain 時: SLL → Ring への昇格(一方向) 2. **Box 21-2 (Hot slab)**: ✅ 有効だが扱いに注意 - hot slab が EMPTY 時の差し替えロジック - shared_pool / SS-Reuse との整合性 3. **Box 21-3 (Minimal meta)**: ⚠️ 後回しでOK - struct 分離は型分岐コスト増 - アクセスパターン限定だけで効果あり - 21-1/2 の結果を見てから判断 4. **Phase 12 との順番**: ✅ 合理的 - アクセスパターン > SuperSlab 数 - Phase 21 → Phase 12 の順で問題なし ### 実装リスク **低リスク**: - C2/C3 のみ変更(他クラスは SLL のまま) - 既存構造を大きく変えない - ENV で A/B テスト可能 **注意点**: - Ring と SLL の境界を明確に - shared_pool / SS-Reuse との整合 - 型分岐が増えすぎないように ### 次のアクション **Phase 21-1 実装開始**: 1. `core/box/hot_ring_cache_box.{h,c}` 作成 2. C2/C3 専用 TlsRingCache 実装 3. Ring → SLL → SuperSlab 階層化 4. ENV: `HAKMEM_TINY_HOT_RING_ENABLE=1` 5. ベンチマーク: 目標 62-65M ops/s (+15-20%) --- --- ## HAKMEM ハング問題調査 (2025-11-16) ### 症状 1. `bench_fixed_size_hakmem 1 16 128` → 5秒以上ハング 2. `bench_random_mixed_hakmem 500000 256 42` → キルされた ### Root Cause **Cross-thread check の always-on 化** (直前の修正) - `core/tiny_free_fast_v2.inc.h:175-204` で ENV ゲート削除 - Single-thread でも毎回 SuperSlab lookup 実行 ### ハング箇所の推定 (確度順) | 箇所 | ファイル:行 | 原因 | 確度 | |------|-----------|------|------| | `hak_super_lookup()` registry probing | `core/hakmem_super_registry.h:119-187` | 線形探索 32-64 iterations / free | **高** | | Node pool exhausted fallback | `core/hakmem_shared_pool.c:394-400` | sp_freelist_push_lockfree fallback の unsafe | 中 | | `tls_sll_push()` CAS loop | `core/box/tls_sll_box.h:75-184` | 単純実装、無限ループはなさそう | 低 | ### パフォーマンス影響 ``` Before (header-based): 5-10 cycles/free After (cross-thread): 110-520 cycles/free (11-51倍遅い!) 500K iterations: 500K × 200 cycles = 100M cycles @ 3GHz = 33ms → Overhead は大きいが単なる遅さ? ``` ### Node pool exhausted の真実 - `MAX_FREE_NODES_PER_CLASS = 4096` - 500K iterations > 4096 → exhausted ⚠️ - しかし fallback (`sp_freelist_push()`) は lock-free で安全 - **副作用であり、直接的ハング原因ではない可能性高い** ### 推奨修正 ✅ **ENV ゲートで cross-thread check を復活** ```c // core/tiny_free_fast_v2.inc.h:175 static int g_larson_fix = -1; if (__builtin_expect(g_larson_fix == -1, 0)) { const char* e = getenv("HAKMEM_TINY_LARSON_FIX"); g_larson_fix = (e && *e && *e != '0') ? 1 : 0; } if (__builtin_expect(g_larson_fix, 0)) { // Cross-thread check - only for MT SuperSlab* ss = hak_super_lookup(base); // ... rest of check } ``` **利点:** - Single-thread ベンチ: 5-10 cycles (fast) - Larson MT: `HAKMEM_TINY_LARSON_FIX=1` で有効 (safe) ### 検証コマンド ```bash # 1. ハング確認 timeout 5 ./out/release/bench_fixed_size_hakmem 1 16 128 echo $? # 124 = timeout # 2. 修正後確認 HAKMEM_TINY_LARSON_FIX=0 ./out/release/bench_fixed_size_hakmem 1 16 128 # Should complete fast # 3. 500K テスト ./out/release/bench_random_mixed_hakmem 500000 256 42 2>&1 | grep "Node pool" # Output: [P0-4 WARN] Node pool exhausted for class 7 ``` ### 詳細レポート - **HANG分析**: `/tmp/HAKMEM_HANG_INVESTIGATION_FINAL.md`