# Phase 74: UnifiedCache hit-path structural optimization (WS=400 SSOT) **Status**: 🟡 DRAFT(設計SSOT / 次の指示書) ## 0) 背景(なぜ今これか) - 現行 baseline(Phase 69): `bench_random_mixed_hakmem_minimal_pgo` = **62.63M ops/s = 51.77% of mimalloc**(`HAKMEM_WARM_POOL_SIZE=16`) - Phase 70(観測SSOT)により、WS=400(Mixed SSOT)では **UnifiedCache miss が極小**であることが確定。 - `unified_cache_refill()` / WarmPool-pop を速くしても **ROI はほぼゼロ**(refill最適化は凍結) - SSOT: `docs/analysis/PHASE70_REFILL_OBSERVABILITY_PREREQS_SSOT.md` - Phase 73(perf stat)により、WarmPool=16 の勝ちは **instruction/branch の微減**が支配的と確定。 - つまり次も「hit-path を短くする」方向が最も筋が良い。 - 詳細: `docs/analysis/PHASE70_71_WARMPOOL16_ANALYSIS.md` 本フェーズの狙いは、**UnifiedCache の hit-path(push/pop)から“踏まなくていい分岐/ロード”を構造で外に追い出す**こと。 ## 1) 目的 / 非目的 **目的** - WS=400 の SSOT workload で **+1〜3%**(単発)を狙う(積み上げで M2=55% へ)。 - “経路が踏まれていない最適化” を避ける(Phase 70 の SSOT を守る)。 **非目的** - `unified_cache_refill()` の最適化(miss が極小なので SSOT では ROI なし)。 - link-out / 大削除による DCE(layout tax で符号反転の前例が多い)。 - route kind を変えて別 workload にする(まず SSOT workload を崩さない)。 ## 2) Box Theory(箱割り) ### 箱の責務 L0: **EnvGateBox** - `HAKMEM_TINY_UC_*` のトグル(default OFF、いつでも戻せる)。 L1: **TinyUnifiedCacheHitPathBox(NEW / 研究箱)** - `unified_cache_push/pop` の **hit-path だけを短くする**(refill/overflow/registryは触らない)。 - 変換点(境界)は 1 箇所: `unified_cache_push/pop` 内で “fast→fallback” を1回だけ行う。 ### 可視化(最小) - `uc_hitpath_fast_hits` / `uc_hitpath_fast_fallbacks` の2カウンタだけ(必要なら)。 - それ以外は `perf stat`(instructions/branches)を正とする。 ## 3) 具体案(優先順) ### P1(低リスク): ローカル変数化で再ロード/依存チェーンを固定する 狙い: - `cache->head/tail/mask/capacity` 等の再ロードを抑制し、**依存チェーンを短く**する。 設計: - `unified_cache_push()` / `unified_cache_pop_or_refill()` の中で - `uint16_t head = cache->head;` のように **ローカルへ落とす** - `next = (x + 1) & mask` の算術を **1回に固定** - `cache->tail = next;` のような store を最後にまとめる 導入: - ENV: `HAKMEM_TINY_UC_LOCALIZE=0/1`(default 0) - 方式: 同一バイナリで ON/OFF(layout tax を最小にするため、分岐は入口1回に限定) リスク: - レジスタ圧上昇で逆に遅くなる可能性 → A/B 必須。 ### P0(中リスク/中ROI): Fast-API 化(enable判定/統計を外に追い出す) 狙い: - hit-path の中に残る “ほぼ不変な判定” を **呼び出し側に追い出し**、`push/pop` を直線化する。 設計: - `unified_cache_push_fast(TinyUnifiedCache* cache, void* base)` のような **最短API** を追加 - 前提: “有効/初期化済み/統計OFF” を呼び出し側で保証 - 失敗時のみ既存 `unified_cache_push()` へ落とす(境界1箇所) 導入: - ENV: `HAKMEM_TINY_UC_FASTAPI=0/1`(default 0) - Fail-fast: 途中でモードが変わったら “safe fallback” へ(bench用途なら abort でも良い) リスク: - call site の増加で layout が動く → GO 閾値は +1.0%(厳しめ)。 ### P2(高リスク/高ROI候補): hot class 限定で slots を TLS 直置き(pointer chase削減) 狙い: - hit-path の `cache->slots` のロード(ポインタ追跡)を消す。 設計: - `TinyUnifiedCache` の “hot class のみ” を別構造に逃がし、TLS 内に `slots[]` を直置き。 - 対象候補: 容量が小さい C4/C5/C6/C7(C2/C3 の 2048 は直置きが重い) リスク: - TLS サイズ増で dTLB/cache が悪化しうる(勝てば大きいが、NO-GO もあり得る)。 ## 4) A/B(SSOT) ### 4.1 ベンチ条件(固定) - `scripts/run_mixed_10_cleanenv.sh`(`ITERS=20000000 WS=400`) - `HAKMEM_WARM_POOL_SIZE=16`(baseline) ### 4.2 GO/NO-GO - **GO**: +1.0% 以上 - **NEUTRAL**: ±1.0%(research box freeze) - **NO-GO**: -1.0% 以下(即 revert) ### 4.3 追加で必ず見る(Phase 73 教訓) - `perf stat`: `instructions`, `branches`, `branch-misses`(勝ち筋が instruction/branch 減なので) - `cache-misses`, `iTLB-load-misses`, `dTLB-load-misses`(layout tax 検知) ## 5) 直近の実装順(推奨) 1. **P1(LOCALIZE)** を小さく入れて A/B(最短で勝ち筋確認) 2. 勝てたら **P0(FASTAPI)** を追加(さらに分岐を外へ) 3. それでも足りなければ **P2(inline slots hot)** を research box として試す ## 6) 退出条件(やめどき) - WS=400 SSOT で `perf` 上の “unified_cache_push/pop” が Top 50 圏外になったら、この系は撤退(Phase 42 の教訓)。 - 3回連続で NEUTRAL/NO-GO が続いたら、次の構造(別層)へ(layout tax の危険が増すため)。