5.9 KiB
5.9 KiB
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.hcore/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(案)
// 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
- Baseline:
判定
- 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(即戻せる)。