# Phase 4 E1: ENV Snapshot Consolidation(設計メモ) ## Status(2025-12-14) - ✅ 実装完了(commit: `88717a873`) - ✅ Mixed A/B(10-run, iter=20M, ws=400): - Baseline: **43.62M** ops/s - Optimized: **45.33M** ops/s - Gain: **+3.92% avg / +4.01% median** - 判定: **GO**(目標 +2.5% をクリア) - 運用: `HAKMEM_ENV_SNAPSHOT=1` は **opt-in(default OFF)** のまま保持(必要ならプリセット昇格) ## 目的 ホットパスで毎回呼ばれている ENV gate(小さな関数)の呼び出し/分岐/TLS参照を **1 回の “snapshot load” に集約**し、 MIXED の「shape 最適化の頭打ち」を越える。 ターゲット(直近 perf self%): - `tiny_c7_ultra_enabled_env()` ≈ 1.28% - `tiny_front_v3_enabled()` ≈ 1.01% - `tiny_metadata_cache_enabled()` ≈ 0.97% - 合計 ≈ **3.26%** ※ self% はそのまま speedup にはならないが、Hot loop で “毎回” 評価される小関数群が占める割合として無視できない。 ## 背景(何が起きているか) - Phase 2–3 の “分岐形(shape)” 最適化は一定効いたが、D3 が NEUTRAL になり **形だけでは頭打ち**。 - 現状は「ENV gate を 3 回別々に呼ぶ」ため、 - 小関数 call(inlining されないケース) - lazy init の分岐(TU-local static / probe window) - TLS 参照(policy hot TLS 等) が積み上がる。 - さらに `__builtin_expect(..., 0)` が **実態(default ON)と逆**の場所があり、分岐予測を損ねている可能性が高い。 ## 非目標 - ルーティングやアルゴリズムの変更(意味を変えない) - Learner の挙動変更(interlock を壊さない) - 常時ログ増加(ワンショット/統計のみ) ## 箱割り(Box Theory) ### L0: EnvSnapshotBox(設定を 1 箱に集約) 新規 Box: - `core/box/hakmem_env_snapshot_box.h` - `core/box/hakmem_env_snapshot_box.c` 責務: - ENV を 1 回だけ読んで “Hot に必要な bool 値” を構造体に凍結する - bench_profile の `putenv()` 後に refresh できる(戻せる) ### L1: Call-site Migration(境界 1 箇所ずつ置換) 既存の ENV gate を “呼ばない” 方向で、以下の call-site を段階的に置換する。 (既存 gate 関数は残し、E1 を OFF に戻せる) 対象(最小セット): - `tiny_c7_ultra_enabled_env()` → snapshot フィールド参照 - `tiny_front_v3_enabled()` → snapshot フィールド参照(or snapshot から front_snap を得る) - `tiny_metadata_cache_enabled()` → snapshot フィールド参照(learner interlock を含んだ “effective” 値) ## API(案) ```c // core/box/hakmem_env_snapshot_box.h typedef struct HakmemEnvSnapshot { bool tiny_c7_ultra_enabled; // ENV: HAKMEM_TINY_C7_ULTRA_ENABLED (default 1) bool tiny_front_v3_enabled; // ENV: HAKMEM_TINY_FRONT_V3_ENABLED (default 1) bool tiny_metadata_cache; // ENV: HAKMEM_TINY_METADATA_CACHE (default 0) bool tiny_metadata_cache_eff; // tiny_metadata_cache && !learner } HakmemEnvSnapshot; bool hakmem_env_snapshot_enabled(void); // ENV: HAKMEM_ENV_SNAPSHOT=0/1 (default 0) const HakmemEnvSnapshot* hakmem_env_snapshot(void); void hakmem_env_snapshot_refresh_from_env(void); // bench_profile putenv sync ``` 設計ノート: - `*_eff` を持たせ、call-site から `small_learner_v2_enabled()` の呼び出しを除去する - `enabled` は refresh 時に 1 回だけ決め、Hot では分岐を増やさない(できれば compile-out) ## 初期化と bench_profile(putenv 問題) bench では `bench_setenv_default()` が `putenv()` を使うため、lazy init が先に走ると “誤った 0” を掴む。 対策は既存方針に合わせる: - `core/bench_profile.h` の最後で `hakmem_env_snapshot_refresh_from_env()` を必ず呼ぶ - `wrapper_env_refresh_from_env()` / `tiny_static_route_refresh_from_env()` と同じ “ENV 同期箱” 扱い ## 実装(置換した call-site) - `core/front/malloc_tiny_fast.h`: - `tiny_c7_ultra_enabled_env()` → snapshot(alloc/free の C7 ULTRA gate) - `tiny_front_v3_enabled()` → snapshot(free 側 front_snap) - `core/box/tiny_legacy_fallback_box.h`: - `tiny_front_v3_enabled()` / `tiny_metadata_cache_enabled()` → snapshot - `core/box/tiny_metadata_cache_hot_box.h`: - `tiny_metadata_cache_enabled()` → snapshot(learner interlock を snapshot 側で処理) ## 移行対象(最小) まずは “毎回評価される” ところを最小パッチで狙う: - `core/front/malloc_tiny_fast.h`(alloc/free の hot path) - `core/box/tiny_legacy_fallback_box.h`(free の第2ホット) - `core/box/tiny_metadata_cache_hot_box.h`(policy hot 入口) - `core/box/free_policy_fast_v2_box.h`(研究箱だが整合) ## A/B(GO/NO-GO) ### ベンチ - Mixed(10-run, iter=20M, ws=400, 1T) - Baseline: `HAKMEM_ENV_SNAPSHOT=0` - Opt: `HAKMEM_ENV_SNAPSHOT=1` ### 判定 - GO: **mean +2.5% 以上**(目標) かつ crash/assert なし - NEUTRAL: ±1% 以内 → research box 維持(default OFF) - NO-GO: -1% 以下 → freeze ### 検証(必須) - perf で `tiny_c7_ultra_enabled_env/tiny_front_v3_enabled/tiny_metadata_cache_enabled` の self% が **明確に減る**こと - 本線プロファイル(MIXED_TINYV3_C7_SAFE)で regression がないこと ## リスクと回避 - **MEDIUM(refactor/置換)**: - 置換は “最小 3 gate” から始め、段階的に広げる - 失敗したら `HAKMEM_ENV_SNAPSHOT=0` で即戻す - **初期化順序**: - bench_profile に refresh を追加して putenv 後の SSOT を保証 - **Learner interlock**: - `tiny_metadata_cache_eff` の計算で learner を必ず抑制 ## 次(Graduate) - 追加で 20-run を回し、問題がなければ `MIXED_TINYV3_C7_SAFE` に `HAKMEM_ENV_SNAPSHOT=1` を **プリセット昇格**する(bench default 注入)。 - rollback は `HAKMEM_ENV_SNAPSHOT=0`(即戻せる)。