# Phase ALLOC-TINY-FAST-DUALHOT-1 設計(C0–C3 を第2ホットとして扱う) ## 背景(現状のボトルネック) FREE-TINY-FAST-DUALHOT-1 により `free` の self% が大きく低下したため、Mixed の次のボトルネックが **alloc 側**に移った。 (例: `tiny_alloc_gate_fast` + `malloc` が合計 ~30% 付近) ここで重要なのは、FREE 側の学びと同じ: - C7 は ULTRA で “第1ホット” - C0–C3 は LEGACY(UC/SFC/SLL 等)で “第2ホット” - “第2ホット” を rare 扱いして `policy snapshot` を毎回踏むと損 --- ## 目的 `malloc_tiny_fast()` の alloc 経路で、C0–C3 については - `small_policy_v7_snapshot()`(policy snapshot) - `route_kind` 判定(switch) を **スキップ**し、LEGACY の最短経路(TLS unified cache hit → refill)へ直行させる。 --- ## 非目標(やらないこと) - C4–C7 のルーティング設計変更(ULTRA/MID/v7 の構造は触らない) - “PGO build 前提の大改造” はやらない(まずは小パッチで A/B) - 統計の常時 ON(global atomic)を入れない(外乱要因になる) --- ## 設計(Box Theory) ### 箱と境界 - 対象ホット関数: `core/front/malloc_tiny_fast.h` の `malloc_tiny_fast(size)` - 境界: `class_idx` 確定直後(size→class の 1 回目)に “第2ホット” を判定して early-exit - 既存の Box は変更しない(`tiny_hot_alloc_fast()` と `tiny_cold_refill_and_alloc()` を再利用) ### 戻せる(A/B) - ENV gate を 1 つ追加する: - `HAKMEM_TINY_ALLOC_DUALHOT=0/1` - 初期は default OFF(A/B しやすくする) ### Fail-Fast / Safety - alloc は cross-thread free と違い “所有者” 概念がないため、Larson ガード不要。 - ただし、将来 C0–C3 を v6/v7 等へ載せる実験に備え、ENV でいつでも OFF に戻せるようにする。 --- ## 具体案(擬似コード) 差し込み位置は `class_idx` を確定し、C7 ULTRA early-exit を試した **直後**。 ```c // C7 ULTRA early-exit (既存) if (class_idx == 7 && tiny_c7_ultra_enabled_env()) { ... } // NEW: C0–C3 DUALHOT if (alloc_dualhot_enabled() && class_idx <= 3) { void* p = tiny_hot_alloc_fast(class_idx); if (p) return p; return tiny_cold_refill_and_alloc(class_idx); } // 既存: policy snapshot + route_kind switch const SmallPolicyV7* policy = small_policy_v7_snapshot(); switch (policy->route_kind[class_idx]) { ... } ``` --- ## 計測手順(GO/NO-GO) ### Gate 1: 健康診断 `scripts/verify_health_profiles.sh` を default 設定(ENV OFF)で 1 回。 ### Gate 2: A/B(Mixed) - Baseline: `HAKMEM_TINY_ALLOC_DUALHOT=0` - Opt: `HAKMEM_TINY_ALLOC_DUALHOT=1` 同一条件で 10-run。中央値と分散を比較。 ### Gate 3: perf(Mixed) 期待: - `tiny_alloc_gate_fast` / `malloc` self% が下がる - `main` が相対的に上がる(= allocator 側の余地を削った証拠) --- ## 成功条件 - Mixed: +2% 以上(または free DUALHOT と同等の “明確な改善”) - C6-heavy: ±2% 以内(回帰なし) - 回帰が出たら default OFF の研究箱として freeze(保持して次の学びに使う) --- ## 実測結果(2025-12-13) 計測条件: - Mixed: `HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE ./bench_random_mixed_hakmem 100000000 400 1`(10-run) - C6-heavy: `HAKMEM_PROFILE=C6_HEAVY_LEGACY_POOLV1 ./bench_mid_large_mt_hakmem 1 10000000 400 1`(10-run) 結果: ### Mixed(MIXED_TINYV3_C7_SAFE) - Baseline(`HAKMEM_TINY_ALLOC_DUALHOT=0`): mean=50.60M, median=50.87M ops/s - Opt(`HAKMEM_TINY_ALLOC_DUALHOT=1`): mean=50.27M, median=50.28M ops/s - 差分(median): **-1.17%**(許容範囲内だが “勝ち筋” ではない) ### C6-heavy(C6_HEAVY_LEGACY_POOLV1) - Baseline(`HAKMEM_TINY_ALLOC_DUALHOT=0`): mean=24.73M, median=24.69M ops/s - Opt(`HAKMEM_TINY_ALLOC_DUALHOT=1`): mean=24.62M, median=24.78M ops/s - 差分(median): **+0.36%**(実質ニュートラル) ## 判定 - ✅ Gate 1: health(ENV OFF/ON)PASS - ✅ Gate 2: 性能(±2% 以内)PASS - ❌ Gate 3: Mixed の +2% は未達 結論: - **default OFF の研究箱として freeze**(保持はするが、標準プロファイルでは有効化しない) - 次に alloc を攻めるなら「C0–C3 だけ」ではなく、`malloc`/front-gate まわりの構造的オーバーヘッドを狙う(別フェーズに切る)