diff --git a/docs/analysis/PHASE11_ENV_SNAPSHOT_MAYBE_FAST_1_AB_TEST_RESULTS.md b/docs/analysis/PHASE11_ENV_SNAPSHOT_MAYBE_FAST_1_AB_TEST_RESULTS.md new file mode 100644 index 00000000..dde50a56 --- /dev/null +++ b/docs/analysis/PHASE11_ENV_SNAPSHOT_MAYBE_FAST_1_AB_TEST_RESULTS.md @@ -0,0 +1,206 @@ +# Phase 11: ENV Snapshot "maybe-fast" API - A/B テスト結果 + +## 判定: **NO-GO** (-8.35%) + +Phase 11 の実装は **大幅な性能低下 (-8.35%)** を引き起こしたため、**rollback 済み**。 + +--- + +## 0. 目的(Phase 11) + +Phase 10 までで `free_tiny_fast()` 側の policy/route 固定費は削減されたが、Perf(Phase 10 後, Mixed)では次が目立っていた: + +- `hakmem_env_snapshot` / `hakmem_env_snapshot_enabled`(合計 ~2%) +- `tiny_front_v3_snapshot_get`(~1%) + +**Phase 11 の狙い**: ENV gate を維持したまま、hot path の参照回数を 1 回に集約し、同時に `tiny_front_v3_snapshot_get()` の ready-check を hot から消すこと。 + +**設計**: +1. `hakmem_env_snapshot_maybe_fast()` API を追加(enabled + snapshot を 1 回で取得) +2. `HakmemEnvSnapshot` に `front_v3_snap` ポインタをキャッシュ(ready-check を消す) +3. hot call sites (`tiny_legacy_fallback_box.h`, `malloc_tiny_fast.h`, `tiny_metadata_cache_hot_box.h`) を maybe-fast API に移行 + +--- + +## 1. A/B テスト結果(Mixed 10-run, clean env) + +### Baseline (Phase 11 前) + +``` +Run 1/10: 52.40 M ops/s +Run 2/10: 51.44 M ops/s +Run 3/10: 52.21 M ops/s +Run 4/10: 51.94 M ops/s +Run 5/10: 51.49 M ops/s +Run 6/10: 50.86 M ops/s +Run 7/10: 51.59 M ops/s +Run 8/10: 51.38 M ops/s +Run 9/10: 51.29 M ops/s +Run 10/10: 51.90 M ops/s + +Mean: 51.65 M ops/s +Median: 51.54 M ops/s +Stdev: 0.46 M ops/s +``` + +### Phase 11 後(最終版, CTOR=1) + +``` +Run 1/10: 47.56 M ops/s +Run 2/10: 47.45 M ops/s +Run 3/10: 47.62 M ops/s +Run 4/10: 47.97 M ops/s +Run 5/10: 47.59 M ops/s +Run 6/10: 45.89 M ops/s +Run 7/10: 47.96 M ops/s +Run 8/10: 47.23 M ops/s +Run 9/10: 47.33 M ops/s +Run 10/10: 46.72 M ops/s + +Mean: 47.33 M ops/s +Median: 47.51 M ops/s +Stdev: 0.62 M ops/s +``` + +### 性能差分 + +| 指標 | Phase 11 前 | Phase 11 後 | 差分 | +|------|-------------|-------------|------| +| 平均 | 51.65 M ops/s | 47.33 M ops/s | **-4.32 M ops/s (-8.35%)** | +| 中央値 | 51.54 M ops/s | 47.51 M ops/s | -4.03 M ops/s (-7.82%) | +| 標準偏差 | 0.46 M ops/s | 0.62 M ops/s | +0.16 M ops/s | + +**判定**: **NO-GO** (目標: +1.0% 以上、実際: **-8.35%**) + +--- + +## 2. 根本原因分析 + +### 2.1 設計の致命的な欠陥 + +Phase 11 の `hakmem_env_snapshot_maybe_fast()` API は、以下の理由で **逆に遅くなった**: + +1. **ctor_mode チェックのオーバーヘッド** + - 既存の `hakmem_env_snapshot_enabled()` は **1 回だけ**呼ばれ、結果が分岐に使われる + - Phase 11 では、`maybe_fast()` を **複数の call site で毎回**呼ぶため、**ctor_mode チェックが累積** + - 特に `tiny_legacy_fallback_free_base()` は free path で **毎回**呼ばれるため、オーバーヘッドが顕著 + +2. **call site の変更による最適化の阻害** + - 元のコード: `if (hakmem_env_snapshot_enabled()) { ... } else { legacy }` + - コンパイラが分岐予測を最適化しやすい(enabled==0 が大半) + - Phase 11: `env = hakmem_env_snapshot_maybe_fast(); if (env) { ... } else { legacy }` + - `maybe_fast()` が **無条件に呼ばれる**ため、gate チェックが毎回発生 + - `ctor_mode` の読み取りが毎回発生(TLS read または global read) + +3. **HAKMEM_ENV_SNAPSHOT_CTOR の未設定** + - 初期実装では `HAKMEM_ENV_SNAPSHOT_CTOR=1` をプロファイルに追加していなかった + - `ctor_mode == -1` のとき、lazy init path が毎回走る(getenv 呼び出しは不要だが、gate チェックは発生) + - `CTOR=1` を追加しても性能は改善せず(-8.35%) + +### 2.2 試行錯誤の履歴 + +1. **Version 1**: `ctor_mode` チェックなし、gate チェックのみ + - 結果: -7.6% (NO-GO) + - 原因: gate チェックが毎回発生 + +2. **Version 2**: `ctor_mode` チェックなし、gate==0 を LIKELY に設定 + - 結果: -8.6% (NO-GO) + - 原因: 分岐予測ヒントが MIXED プロファイル(ENV_SNAPSHOT=1)で逆効果 + +3. **Version 3**: `ctor_mode` チェックを復活(既存の `hakmem_env_snapshot_enabled()` と同等) + - 結果: -5.9% (NO-GO) + - 原因: `ctor_mode` チェック自体がオーバーヘッド + +4. **Final**: `HAKMEM_ENV_SNAPSHOT_CTOR=1` をプロファイルに追加 + - 結果: **-8.35% (NO-GO)** + - 原因: `ctor_mode==1` でも、**`maybe_fast()` 呼び出し自体がオーバーヘッド** + +--- + +## 3. 正しいアプローチ(Phase 11 では不可能) + +Phase 11 の設計は **根本的に誤り**。正しくは: + +1. **`maybe_fast()` を関数の入口で 1 回だけ呼ぶ** + - 複数の call site で再利用する(ローカル変数にキャッシュ) + +2. **しかし、`tiny_legacy_fallback_free_base()` は free path で毎回呼ばれる** + - そこで `maybe_fast()` を呼ぶと、毎回 `ctor_mode` チェックが発生 + - 既存のコードでは、`hakmem_env_snapshot_enabled()` が **1 回だけ**呼ばれる設計になっている + +3. **結論**: **Phase 11 の maybe-fast API は構造的に不可能** + - ENV snapshot 参照の固定費削減は、**call site の変更ではなく、ENV snapshot 自体の最適化**(例: TLS → register cache)が必要 + +--- + +## 4. Perf 確認(未実施) + +Phase 11 が -8.35% の大幅な性能低下を引き起こしたため、perf 確認は実施せず。 + +--- + +## 5. 健康診断(未実施) + +NO-GO のため、健康診断は実施せず。 + +--- + +## 6. Rollback 状況 + +Phase 11 の全変更を rollback 済み: + +```bash +git checkout -- core/bench_profile.h core/box/hakmem_env_snapshot_box.c \ + core/box/hakmem_env_snapshot_box.h core/box/tiny_legacy_fallback_box.h \ + core/box/tiny_metadata_cache_hot_box.h core/front/malloc_tiny_fast.h +``` + +--- + +## 7. 次のステップ + +Phase 11 は **設計ミス** のため、放棄。 + +ENV snapshot 参照の固定費削減(~2-3%)を狙う場合、以下の代替アプローチを検討: + +1. **ENV snapshot を TLS → レジスタにキャッシュ** + - 関数入口で 1 回だけ TLS read し、関数内で再利用 + - ただし、`tiny_legacy_fallback_free_base()` などの **inline 関数では効果が薄い** + +2. **ENV snapshot をコンストラクタで確定** + - `HAKMEM_ENV_SNAPSHOT_CTOR=1` を常に有効化 + - gate を **const に昇格**(ただし、ENV の動的変更が不可能になる) + +3. **ENV snapshot 自体を廃止し、個別 gate に戻す** + - Phase 4 E1 の ENV snapshot は +3.92% の効果があったが、その後のフェーズでオーバーヘッドが累積している可能性 + - ただし、rollback は大規模 + +4. **別の最適化ターゲットを探す** + - Perf の最新プロファイルで、次の高コストパスを特定 + +--- + +## 8. 教訓 + +1. **inline 関数で毎回呼ばれる helper は、1 命令でも重い** + - `hakmem_env_snapshot_maybe_fast()` は 2-3 命令だが、free path で毎回呼ばれると -8% の劣化 + +2. **ENV gate の最適化は、call site ではなく gate 自体で行うべき** + - call site を変更すると、コンパイラの最適化を阻害する可能性が高い + +3. **Constructor init (CTOR=1) は万能ではない** + - gate が確定しても、**呼び出しコスト**は残る + +4. **A/B テストは必須** + - Phase 11 は "理論的には速くなるはず" だったが、実測で -8.35% の劣化 + - 小さな変更でも、必ず A/B テストで検証すること + +--- + +## まとめ + +Phase 11 の ENV Snapshot "maybe-fast" API は、**-8.35% の大幅な性能低下**を引き起こし、**NO-GO** と判定。 + +**原因**: `maybe_fast()` 呼び出しが free path で毎回発生し、`ctor_mode` チェックのオーバーヘッドが累積。 + +**結論**: Phase 11 は**設計ミス**であり、rollback 済み。ENV snapshot 参照の固定費削減は、別のアプローチ(TLS → register cache, constructor 確定など)を検討する必要がある。