# シミュレーション速度最適化履歴 (Performance Optimization History)

本ドキュメントは SmTIAS-LightSim のシミュレーション・可視化処理の **実行時間最適化** の試行を時系列で記録する．

目的:

- 「どの仮説でどの最適化を入れ，実時間がどう変わったか」を **数値で残す**．
- **効かなかった試行も同等に記録**し，同じ最適化を繰り返し試す事態を防ぐ．
- 各試行の **学び** を明示し，次に取り組むべきボトルネックを判断する根拠を蓄積する．

関連ドキュメント:

- 関連: [TEST_01 キャリブレーション検証履歴](TEST_01_キャリブレーション検証履歴.md)（精度側の試行）
- 関連: [TEST_02 シミュレーション実行記録](TEST_02_シミュレーション実行記録.md)（output/ 索引）

## 大前提 (Constraints)

| 項目 | 値 |
| --- | --- |
| ターゲット環境 | Windows 11 Pro 10.0.22631 / PowerShell 5.1 |
| GPU | Intel HD Graphics 520（NVIDIA 非搭載） |
| Mitsuba CUDA variant | **使用不可**（CUDA 対応 GPU が無いため） |
| 精度方針 | **現精度（spp/max_depth）を維持したまま実行時間のみ短縮**．サンプル数削減やサンプラ変更による「分散↓→ spp 削減」系の最適化は対象外． |
| 数値同等性 | 各最適化試行は `luminance.npy`（および bake 出力）の数値同等性を確認した上で適用する． |

## ベンチマーク条件 (Benchmark Conditions)

すべての試行は以下の固定条件で測定する（条件を変えた場合は当該試行内に明記）．

### 標準ベンチマーク

| 項目 | 値 |
| --- | --- |
| **標準シーン** | `configs/scene_baseline.yaml`（メイン構成．`llvm_ad_rgb` / spp=1024 / max_depth=8） |
| **B1: sm-render コマンド** | `uv run sm-render configs/scene_baseline.yaml --output output/_bench/luminance.npy` |
| **B2: sm-visualize (path bake) コマンド** | `uv run sm-visualize configs/scene_baseline.yaml --bake-mode path --bake-samples 16 --bake-cache output/_bench/bake.npz --screenshot output/_bench/bake.png --no-show` |
| **計測方法** | `Measure-Command { … }` の `TotalSeconds`．**warm-up 1 回を捨て，3 回測って中央値**を採用． |
| **環境** | 計測中は他の重い処理を閉じる．電源プロファイル `High performance` 推奨． |
| **出力先** | `output/_bench/`（gitignore 配下，数値のみドキュメントに転記） |

### 計測コマンド（PowerShell スニペット）

```powershell
# B1: sm-render
1..4 | ForEach-Object {
  $t = Measure-Command {
    uv run sm-render configs/scene_baseline.yaml `
      --output output/_bench/luminance.npy
  }
  "$_  $($t.TotalSeconds) s"
}

# B2: path bake
1..4 | ForEach-Object {
  Remove-Item output/_bench/bake.npz -ErrorAction SilentlyContinue
  $t = Measure-Command {
    uv run sm-visualize configs/scene_baseline.yaml `
      --bake-mode path --bake-samples 16 `
      --bake-cache output/_bench/bake.npz `
      --screenshot output/_bench/bake.png --no-show
  }
  "$_  $($t.TotalSeconds) s"
}
```

> 1 回目は warm-up（変動が大きい）として捨て，2〜4 回目の中央値を採用する．

### 数値同等性検証 (Equivalence Check)

各試行で出力に対し以下を確認する：

| 出力 | 同等性基準 |
| --- | --- |
| `luminance.npy` | **平均相対誤差 < 1e-6**（決定論的最適化＝同じビット列出力が理想），MC サンプラの初期化規約が変わる場合は **5σ 以内の統計同等性**（同 spp で再計測して比較） |
| `bake.npz`（face_values） | 同上 |

## 俯瞰サマリ (Overall Progression)

時系列の主要計測値．**速度比** = ベースライン時間 / 当該試行時間（大きいほど速い）．

| ID | 試行 | B1: sm-render | B1 速度比 | B2: path bake | B2 速度比 | 数値同等性 |
| --- | --- | --- | --- | --- | --- | --- |
| Speed-0 | ベースライン（LLVM JIT 復元後） | **507.86 s** | 1.0× | **55.97 s** | 1.0× | — |

（試行を追加するごとに行を append）

## イテレーション履歴 (Iteration Log)

### イテレーションのテンプレート

各試行は以下の見出しで記述する：

- **日付**: YYYY-MM-DD
- **コミット**: `<short-hash>`（試行が完了した時点のコミット）
- **背景・問題意識**: なぜこの最適化を考えたか
- **仮説（技術的根拠）**: なぜ速くなると考えるか（JIT 経路・並列化機構等の物理的・実装的根拠）
- **試した変更**: コード/設定の差分（ファイル名・行レベルで具体的に）
- **期待した効果**: 何倍速くなることを期待したか
- **計測コマンド**: 実行したコマンド全文
- **計測結果**: 3 回計測の中央値（warm-up 除く．生データも参考に記載）
- **数値同等性**: luminance.npy / bake.npz の比較結果
- **解釈**: なぜ効いた／効かなかったか
- **学び**: 次の試行への含意
- **次のアクション**: 次にやること

---

### Speed-0: ベースライン計測 (Baseline)

- **日付**: 2026-05-28
- **コミット**: `4e93638`（feature/baseline-calibration の最新．feature/perf-optimization から派生）
- **背景・問題意識**: 改善前の実時間を確定する．これ以降の試行はすべてこの値との比較で評価する．
- **仮説（技術的根拠）**: なし（計測のみ）．

#### 計測前に判明した重大事項（環境破綻と復旧）

**現象**: 計測を開始しようとしたところ，sm-render が以下のエラーで即時クラッシュした．

```
jitc_llvm_init(): LLVM API initialization failed ..
ImportError: jit_init_thread_state(): the LLVM backend is inactive because the LLVM shared library
("LLVM-C.dll") could not be found! Set the DRJIT_LIBLLVM_PATH environment variable to specify its path.
```

`feature/baseline-calibration`（前ブランチ）でも同じエラー → **コード変更に依存しない環境問題**であることが確定．

**調査結果**:

| 項目 | 状態 |
| --- | --- |
| `LLVM-C.dll` の実在 | **あり**: `C:\Program Files\LLVM\bin\LLVM-C.dll`（過去にインストール済み） |
| `DRJIT_LIBLLVM_PATH` 環境変数 | **未設定**（User / Machine 両方とも） |
| PATH 内の LLVM bin | **未登録** |
| sm-visualize の direct bake | **動く**（baker.py が `scalar_rgb` を内部固定するため LLVM 不要） |
| sm-render | **クラッシュ**（scene_baseline.yaml の `variant: llvm_ad_rgb` が JIT 必須） |

**解釈**: 過去のキャリブ作業時には何らかの方法で `DRJIT_LIBLLVM_PATH` が設定されていた（手動セットアップ／別シェル等）が，現在の Windows 環境では消えている．LLVM 自体は `C:\Program Files\LLVM\bin\` に残っていたため，**環境変数を再設定するだけ**で復旧可能と判明．

**復旧手順**:

```powershell
# User scope に永続化（1 度だけ）
[Environment]::SetEnvironmentVariable(
  'DRJIT_LIBLLVM_PATH',
  'C:\Program Files\LLVM\bin\LLVM-C.dll',
  'User'
)
```

> 注意: 既に起動済みのプロセスには反映されない．新規 PowerShell を開いた時点から有効化．本セッションの計測では各コマンド先頭で `$env:DRJIT_LIBLLVM_PATH = '...'` を明示設定して回避．

**復旧確認**:

```
DRJIT_LIBLLVM_PATH: C:\Program Files\LLVM\bin\LLVM-C.dll
mi.variant(): llvm_ad_rgb
JIT activation: OK
```

#### 計測結果

**ベンチ環境**: Windows 11 Pro 22631 / PowerShell 5.1 / Intel HD Graphics 520 / 電源プロファイル「バランス」（High performance への変更は実施せず，TEST_04 全試行で同条件）．

**B1: sm-render**（`configs/scene_baseline.yaml`，`llvm_ad_rgb`，spp=1024，max_depth=8）

| ラン | 時間 (s) |
| --- | --- |
| warm-up (run 0, 捨) | 524.09 |
| run 1 | 507.86 |
| run 2 | 502.32 |
| run 3 | 527.19 |
| **中央値** | **507.86 s ≈ 8.46 分** |

範囲 502.32〜527.19 s（±2.4%），安定．

**B2: path bake**（`configs/scene_baseline.yaml`，`--bake-mode path --bake-samples 16`，`scalar_rgb` 固定）

| ラン | 時間 (s) |
| --- | --- |
| warm-up (run 0, 捨) | 52.54 |
| run 1 | 55.97 |
| run 2 | 65.48 |
| run 3 | 53.22 |
| **中央値** | **55.97 s** |

範囲 53.22〜65.48 s（±10%），B1 より変動大．bake 自体が短時間で PyVista screenshot や I/O オーバーヘッドの寄与が相対的に大きいため．

**出力ファイル ハッシュ・統計（数値同等性検証の基準値）**:

| ファイル | SHA256 | サイズ | 統計 |
| --- | --- | --- | --- |
| `output/_bench/luminance.npy` | `D8B234BB…D4A17AF` | 2,097,280 B | shape=(512,512), float64, mean=147.693, std=46.158, max=233.626, min=0.0 |
| `output/_bench/bake.npz` | `0F28BC6F…BB3A4AA7` | 222,702 B | per-face irradiance, 全メッシュ |

#### JIT 状態診断（最終）

| 項目 | 値 |
| --- | --- |
| `mi.set_variant("llvm_ad_rgb")` | 成功 |
| `mi.variant()` 戻り値 | `llvm_ad_rgb` |
| `DRJIT_LIBLLVM_PATH` 環境変数 | `C:\Program Files\LLVM\bin\LLVM-C.dll`（User scope 永続化済み） |
| `jitc_llvm_init` 警告 | 出ない（child プロセスで `$env:DRJIT_LIBLLVM_PATH` を明示設定した場合） |

#### 解釈

- **B1 中央値 507.86 s ≈ 8.46 分** が現状の sm-render 1 回の所要時間．これは「LLVM JIT 復元後」の値．JIT 復元前は実行不能だったので「効果無限大」とは言えるが，過去キャリブ時の値と等しいと推定（同条件のはず）．
- **B2 中央値 55.97 s** は path bake samples=16 の値．samples=64 や 256 にすると線形に伸びると予想（baker.py のループ構造から）．
- B1 と B2 で **9.1×** の差があるが，これは spp の差（1024 vs 16 per-face MC）と評価対象規模（512² 画像 vs 全メッシュ per-face）の違いによる．直接比較は意味なし．それぞれ独立に最適化対象．

#### 学び

1. **環境変数依存の脆弱性が露呈**: `DRJIT_LIBLLVM_PATH` 未設定で sm-render が即死する．`docs/02_ENV` か README に明記すべき．Speed-1 の方向性に影響．
2. **B1 を 3× 以上速くするには JIT を本物の wavefront 化（バッチ評価）が必要**: 既に `llvm_ad_rgb` 経路に乗っている以上，「JIT 有効化」自体は完了している．純粋な JIT 経路有効化だけでこれ以上速くなる伸びしろは少ない可能性（要検証）．
3. **B2 の path bake は per-face Python ループが支配的**: samples=16 で 56 秒は十分速いが，samples を増やすと急激に伸びる．Speed-2（wavefront 化）の効果は samples 数に比例して大きくなる．

#### 次のアクション

Speed-1 の方向性を **再定義**:

- 当初想定: 「LLVM JIT を有効化する → 自動的に数倍速くなる」  
  → **既に LLVM JIT に乗っていることが判明**したため，純粋な「JIT 有効化」だけでは追加の高速化は得られない可能性大．
- 新方向: **「環境変数未設定でも自動 fallback する仕組み」＋「JIT 経路で確実に走っていることをログで明示」** を Speed-1 として実装．これは速度自体は変えないが，再発防止・診断容易化の意味で価値あり．
- **本命の高速化レバーは Speed-2（path bake wavefront 化）に移る**．B2 を 10× 以上速くする道が残る．B1 は Mitsuba 内部の最適化が既に効いているので，追加の打ち手は限定的．

---

### Speed-1: LLVM JIT 自動検出と診断ログ強化 (Robust JIT Activation)

> **方向修正**: Speed-0 の調査で「LLVM JIT は復元後ベースラインに既に乗っている」ことが判明したため，本イテレーションは「JIT 有効化による高速化」ではなく「**環境破綻の再発防止＋診断容易化**」に再定義する．純粋な実行時間短縮ではなく，**今後の試行を安全に進めるための土台整備**．

- **日付**: 未実施
- **コミット**: 未実施
- **背景・問題意識**: Speed-0 計測前に判明したとおり，`DRJIT_LIBLLVM_PATH` 未設定だと sm-render が即死する．現在は User scope に永続化したが，新規 PC・別ユーザー・CI 環境で同じ問題が再発する．また「現在 JIT 経路に乗っているか」が見えにくく，scalar fallback で密かに遅くなっているケースを取り逃がすリスクがある．
- **仮説（技術的根拠）**:
  - **(a) 自動検出**: `C:\Program Files\LLVM\bin\LLVM-C.dll` 等の標準パスを `mitsuba.set_variant` 前に Python 側で探索し，見つかれば `os.environ['DRJIT_LIBLLVM_PATH']` をプロセス内で設定する．これにより環境変数未設定でも JIT が起動する．
  - **(b) 診断ログ**: `mi.set_variant` 成功時に「実際の variant」と「JIT backend の稼働状態」をログ出力．scalar fallback 時は明示的に警告する．
- **試した変更**: 未実施（実装予定）:
  - `src/sm_tias_lightsim/render/runner.py`: variant 設定前に LLVM-C.dll 自動探索ロジックを追加．候補パス: 環境変数 `DRJIT_LIBLLVM_PATH` → `C:\Program Files\LLVM\bin\LLVM-C.dll` → `C:\Program Files (x86)\LLVM\bin\LLVM-C.dll` → `LLVM_DIR/bin/LLVM-C.dll` 等．発見時に `os.environ` 更新．
  - `src/sm_tias_lightsim/visualize/baker.py`: 同等のロジック（path bake は scalar_rgb 固定だが，将来 wavefront 化で LLVM 経路に乗るため統一）．
  - `docs/02_ENV/ENV_02_環境構築手順.md`: LLVM のインストール手順と `DRJIT_LIBLLVM_PATH` 設定を明記．
  - テスト: LLVM-C.dll の探索ロジックを単体テスト（モックパスで Test-Path 相当を確認）．
- **期待した効果**:
  - B1 / B2 の **計測値は不変**（既に JIT 経路に乗っているため）．
  - 再現性向上：環境変数未設定の新環境でも自動復旧．
  - 診断容易化：JIT 経路で走っていることがログから確認できる．
- **計測コマンド**: ベンチマーク条件参照（Speed-0 と同じ条件）
- **計測結果**: 未実施
- **数値同等性**: 未確認（luminance.npy のハッシュが Speed-0 と一致すること）
- **解釈**: 未実施
- **学び**: 未実施
- **次のアクション**: 未定

---

### Speed-2: path bake wavefront 化 (Wavefront Bake)

- **日付**: 未実施
- **コミット**: 未実施
- **背景・問題意識**: `src/sm_tias_lightsim/visualize/baker.py` の path bake は per-face で hemisphere モンテカルロ積分を回しており，`scalar_rgb` variant 固定．現状で「高 samples が非現実的」と CLAUDE.md にも明記されているように，最大のボトルネックの 1 つ．
- **仮説（技術的根拠）**: per-face ループを `Vector3f` バッチに置換し，`llvm_ad_rgb`（JIT 有効）で 1 回のベクトル化レイ評価に変換すれば，**10〜100×** の高速化が見込める．精度は不変（同じ式を別の評価戦略で解くだけ）．
- **試した変更**: 未実施
- **期待した効果**: B2 (path bake) で **10×** 以上．B1 (sm-render) には影響なし．
- **計測コマンド**: ベンチマーク条件参照
- **計測結果**: 未実施
- **数値同等性**: 未確認（bake.npz の face_values が ±5σ 以内）
- **解釈**: 未実施
- **学び**: 未実施
- **次のアクション**: 未定

---

## 終了条件 (Exit Criteria)

以下のいずれかに到達したらこのドキュメントの初版（Speed-0〜Speed-2）を完了とする．

- B1 (sm-render) が **3× 以上**速くなる，もしくは「JIT は既に走っており LLVM 経路に乗っている」ことが確認できる．
- B2 (path bake) が **10× 以上**速くなる，もしくは「wavefront 化に必要な前提（per-ray の独立性等）が破れているため不可」と判明する．
- 数値同等性が破れた最適化は不採用とする．

それ以降の最適化（denoiser・サンプラ・クラウド GPU 等）は Speed-3 以降で別途検討する．
