# Phase 5 E5: Post E4-Combined Next Instructions(次の指示書) ## Status(2025-12-14 / E5-2 FREEZE 反映) - Baseline(Mixed, 20M iters, ws=400): **47.34M ops/s**(E4-1+E4-2 ON) - Hot spots(self%): - `free`: **37.56%** - `tiny_alloc_gate_fast`: **13.73%** - `malloc`: **12.95%** - `tiny_region_id_write_header`: **6.97%** - `hakmem_env_snapshot_enabled`: **4.29%** - `tiny_get_max_size`: **4.24%** 狙い: “形” 最適化は一段落。次は **free 内部** と **ヘッダ書き込み**、そして **ENV snapshot gate の常時コスト**を削る。 Update: - E5-1(Free Tiny Direct Path)✅ GO(+3.35% mean / +3.36% median) - E5-2(Header write-once)⚪ NEUTRAL → FREEZE - E5-4(Malloc Tiny Direct)⚪ NEUTRAL → FREEZE - E6(ENV snapshot branch-shape fix)❌ NO-GO → FREEZE(-1.71%) - E7(Frozen box prune / baseline diet)❌ NO-GO(-3%台)→ 差し戻し - 次の芯: “削る/分岐形” ではなく、再び **重複排除(境界の一本化)** か **大きい構造変更** を探す --- ## Step 0: Baseline 固定(Mixed) ```sh HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE \ HAKMEM_FREE_WRAPPER_ENV_SNAPSHOT=1 \ HAKMEM_MALLOC_WRAPPER_ENV_SNAPSHOT=1 \ ./bench_random_mixed_hakmem 20000000 400 1 ``` 以後の A/B は必ず同一バイナリで: - A: `E5_* = 0` - B: `E5_* = 1` --- ## Step 1: perf で “free の中身” を割る(必須) ```sh HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE perf record -F 99 -- \ ./bench_random_mixed_hakmem 20000000 400 1 perf report --stdio --no-children ``` 次に `free` だけを掘る: ```sh perf report --stdio --no-children --symbol free ``` 目的: - `free` の中で **真に重い行/分岐**を特定し、E5-1 の境界(箱の切り方)を決める。 --- ## E5-1(優先A): free() 内部の “Tiny 直通” を一本化 ### 仮説 `free` は依然トップだが、wrapper での “tiny 判定→tiny free” がまだ重い(チェック/分岐/再判定が残っている)。 ### 方針(箱理論) - **L0 SplitBox**: `header_magic` / `class_idx` が valid なときだけ Tiny 直通(fail-fast) - **L1 HotBox**: Tiny の same-thread TLS push だけ(副作用ゼロ) - **L1 ColdBox**: 既存の fallback(pool/mid/large/invalid header) ### 実装ルール - 境界は 1 箇所(`free()` wrapper の先頭分岐で確定) - `ENV gate`: `HAKMEM_FREE_TINY_DIRECT=0/1`(default 0 / preset(MIXED)=1) - 可視化はカウンタのみ(`direct_hit`, `direct_miss`, `invalid_header`) ### GO/NO-GO - Mixed 10-run mean: - GO: **+1.0% 以上** - ±1.0%: NEUTRAL(freeze) - -1.0% 以下: NO-GO(freeze) --- ## E5-2: Header write-once(⚪ NEUTRAL → FROZEN) 結論: - E5-2 は **NEUTRAL**(branch overhead ≈ savings)なので **freeze**。 - 以後は追わず、次は E5-4 を優先する。 参照: - Design: `docs/analysis/PHASE5_E5_2_HEADER_REFILL_ONCE_DESIGN.md` - Results: `docs/analysis/PHASE5_E5_2_HEADER_REFILL_ONCE_AB_TEST_RESULTS.md` ### 仮説 `tiny_region_id_write_header` は “正しいが高頻度”。 ブロックは同一クラス内で再利用されるので、ヘッダは **初回だけ**書けば足りる。 ### 方針(箱理論) - **HeaderPrefillBox**(cold/refill 境界)で “ブロック生成時” に header をセット - alloc hot path は `base+1` 返却のみ(header write をしない) ### 安全ゲート - `ENV gate`: `HAKMEM_TINY_HEADER_PREFILL=0/1`(default 0) - Fail-fast: - “prefill された slab” だけ skip を許可 - prefill 未完のブロックは従来 `tiny_region_id_write_header()` にフォールバック ### A/B - Mixed 10-run + health profiles - 期待: +1〜3%(ヘッダ書き込み + 関連分岐の削減) --- ## E5-4(次の芯): Malloc Tiny Direct(E5-1 の alloc 側複製) 指示書: - `docs/analysis/PHASE5_E5_4_MALLOC_TINY_DIRECT_NEXT_INSTRUCTIONS.md` --- ## E5-3(DEFER): `hakmem_env_snapshot_enabled()` の分岐形を “enabled 前提” に寄せる ### 背景 `MIXED_TINYV3_C7_SAFE` では `HAKMEM_ENV_SNAPSHOT=1` が常用になったため、 現状の `if (__builtin_expect(hakmem_env_snapshot_enabled(), 0))` は **hint が逆**になり得る。 ### 方針 同じ意味で分岐形だけ変える(箱の外形最適化): - `if (__builtin_expect(!hakmem_env_snapshot_enabled(), 0)) { legacy; } else { snapshot; }` - もしくは `*_cold()` に legacy を追い出す(noinline,cold) ### ENV / 戻せる - `ENV gate`: `HAKMEM_ENV_SNAPSHOT_SHAPE=0/1`(default 0) - まず `malloc_tiny_fast.h` の 5 箇所と、`tiny_legacy_fallback_box.h` / `tiny_metadata_cache_hot_box.h` を対象にする ### GO/NO-GO - Mixed 10-run mean で **+1.0% 以上**なら採用候補 - 期待: +0.5〜2.0%(mispredict 回避) --- ## Step 2: 健康診断(必須) ```sh scripts/verify_health_profiles.sh ``` --- ## Step 3: 昇格(勝ち箱のみ) - `core/bench_profile.h` の `MIXED_TINYV3_C7_SAFE` に default 化(opt-out 可能) - `docs/analysis/ENV_PROFILE_PRESETS.md` に A/B と rollback を追記 - `CURRENT_TASK.md` を更新(結果と “次の芯” を 1 行で)