Deep CFR evaluation 배칭 및 병렬화 추가
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# Deep CFR Batched Evaluation 2026-05-07
|
||||
|
||||
Run directory:
|
||||
|
||||
`/mnt/2tbhdd/coolrl-lost-cities-runs/2026-05-07_032944_deep_cfr_batched_eval_cuda_postprocess_1iter`
|
||||
|
||||
Command:
|
||||
|
||||
```bash
|
||||
uv run python -m coolrl_lost_cities.games.classic.deep_cfr.cli train \
|
||||
--config configs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability.yaml \
|
||||
--checkpoint-dir /mnt/2tbhdd/coolrl-lost-cities-runs/2026-05-07_032944_deep_cfr_batched_eval_cuda_postprocess_1iter \
|
||||
--max-iterations 1 \
|
||||
--eval-every 1 \
|
||||
--save-latest-only
|
||||
```
|
||||
|
||||
The config used `evaluation.batch_size: 64` and `evaluation.device: trainer`.
|
||||
The base run device was CUDA.
|
||||
|
||||
## Summary
|
||||
|
||||
The run completed one training iteration and evaluated immediately.
|
||||
|
||||
| Metric | Value |
|
||||
| --- | ---: |
|
||||
| `iteration_seconds` | 21.322356 |
|
||||
| `evaluation_seconds` | 14.834096 |
|
||||
| `traversal_seconds` | 3.850308 |
|
||||
| `advantage_train_seconds` | 1.778991 |
|
||||
| `strategy_train_seconds` | 0.847232 |
|
||||
|
||||
Compared with the previous batch-size-1 CUDA check, evaluation time changed
|
||||
from about 61.826s to about 14.834s.
|
||||
|
||||
## Opponent Timing
|
||||
|
||||
| Opponent | elapsed | avg len | policy turns | network | postprocess | encoding | legal mask | opponent act |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| `safe_heuristic_strict` | 3.971 | 1000.0 | 50000 | 0.195 | 0.251 | 0.147 | 0.084 | 3.168 |
|
||||
| `safe_heuristic` | 3.685 | 997.5 | 49876 | 0.199 | 0.257 | 0.148 | 0.084 | 2.868 |
|
||||
| `safe_heuristic_loose` | 3.330 | 980.8 | 49038 | 0.194 | 0.250 | 0.145 | 0.082 | 2.536 |
|
||||
| `noisy_safe` | 2.902 | 955.9 | 47784 | 0.195 | 0.250 | 0.144 | 0.079 | 2.109 |
|
||||
| `random` | 0.797 | 640.4 | 31968 | 0.154 | 0.204 | 0.095 | 0.053 | 0.212 |
|
||||
| `passive_discard` | 0.138 | 172.2 | 8562 | 0.032 | 0.042 | 0.025 | 0.014 | 0.004 |
|
||||
|
||||
## Totals
|
||||
|
||||
| Metric | Value |
|
||||
| --- | ---: |
|
||||
| `eval_elapsed_seconds` | 14.824448 |
|
||||
| `eval_policy_turns` | 237228 |
|
||||
| `eval_policy_network_seconds` | 0.968006 |
|
||||
| `eval_policy_postprocess_seconds` | 1.253108 |
|
||||
| `eval_policy_encoding_seconds` | 0.702534 |
|
||||
| `eval_policy_legal_mask_seconds` | 0.396494 |
|
||||
| `eval_opponent_act_seconds` | 10.896567 |
|
||||
| `eval_apply_action_seconds` | 0.068075 |
|
||||
| `eval_diagnostics_seconds` | 0.060940 |
|
||||
|
||||
## Notes
|
||||
|
||||
Batched network inference removed the previous batch-size-1 CUDA network
|
||||
bottleneck. After batching, safe heuristic opponent action time became the
|
||||
largest remaining eval cost for safe heuristic opponents.
|
||||
|
||||
The first batched implementation exposed a postprocess synchronization cost
|
||||
from per-row entropy calculation. Moving entropy calculation into torch batch
|
||||
postprocess reduced that cost before this run.
|
||||
|
||||
## Opponent Parallel Evaluation
|
||||
|
||||
Run directory:
|
||||
|
||||
`/mnt/2tbhdd/coolrl-lost-cities-runs/2026-05-07_033925_deep_cfr_batched_eval_parallel_1iter`
|
||||
|
||||
Command:
|
||||
|
||||
```bash
|
||||
uv run python -m coolrl_lost_cities.games.classic.deep_cfr.cli train \
|
||||
--config configs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability.yaml \
|
||||
--checkpoint-dir /mnt/2tbhdd/coolrl-lost-cities-runs/2026-05-07_033925_deep_cfr_batched_eval_parallel_1iter \
|
||||
--max-iterations 1 \
|
||||
--eval-every 1 \
|
||||
--save-latest-only
|
||||
```
|
||||
|
||||
This run used `evaluation.num_workers: 4`, `evaluation.batch_size: 64`, and
|
||||
`evaluation.device: trainer` on CUDA.
|
||||
|
||||
| Metric | Batched sequential | Batched opponent-parallel |
|
||||
| --- | ---: | ---: |
|
||||
| `iteration_seconds` | 21.322356 | 12.854383 |
|
||||
| `evaluation_seconds` | 14.834096 | 6.420402 |
|
||||
| `traversal_seconds` | 3.850308 | 3.852627 |
|
||||
| `advantage_train_seconds` | 1.778991 | 1.746623 |
|
||||
| `strategy_train_seconds` | 0.847232 | 0.819998 |
|
||||
|
||||
Opponent elapsed values from the parallel run:
|
||||
|
||||
| Opponent | elapsed | opponent act | network | avg len |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| `safe_heuristic_strict` | 4.121 | 3.225 | 0.196 | 1000.0 |
|
||||
| `safe_heuristic` | 4.029 | 2.863 | 0.281 | 997.5 |
|
||||
| `safe_heuristic_loose` | 3.807 | 2.631 | 0.286 | 981.6 |
|
||||
| `noisy_safe` | 3.026 | 2.141 | 0.211 | 951.7 |
|
||||
| `random` | 1.130 | 0.212 | 0.253 | 639.0 |
|
||||
| `passive_discard` | 0.417 | 0.004 | 0.114 | 172.2 |
|
||||
|
||||
Parallel eval reduced measured eval wall time from about 14.83s to about
|
||||
6.42s, roughly `2.31x` faster for this one-iteration profile.
|
||||
@@ -62,3 +62,92 @@ still smaller than policy network time.
|
||||
|
||||
`apply_action_seconds`, `diagnostics_seconds`, and `final_scoring_seconds` were
|
||||
small in this run.
|
||||
|
||||
## CPU vs CUDA Evaluation Check
|
||||
|
||||
This check compared `--device cpu` and `--device cuda` on the same base
|
||||
configuration after the evaluation breakdown metrics were available. The base
|
||||
configuration was:
|
||||
|
||||
`configs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability.yaml`
|
||||
|
||||
Both runs used one training iteration and ran evaluation on that iteration.
|
||||
The base configuration's evaluation settings were kept at 100 games per
|
||||
opponent and the six configured opponents.
|
||||
|
||||
CPU run directory:
|
||||
|
||||
`/mnt/2tbhdd/coolrl-lost-cities-runs/2026-05-07_eval_device_cpu_1iter`
|
||||
|
||||
CPU command:
|
||||
|
||||
```bash
|
||||
uv run python -m coolrl_lost_cities.games.classic.deep_cfr.cli train \
|
||||
--config configs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability.yaml \
|
||||
--checkpoint-dir /mnt/2tbhdd/coolrl-lost-cities-runs/2026-05-07_eval_device_cpu_1iter \
|
||||
--max-iterations 1 \
|
||||
--eval-every 1 \
|
||||
--save-latest-only \
|
||||
--device cpu
|
||||
```
|
||||
|
||||
CUDA run directory:
|
||||
|
||||
`/mnt/2tbhdd/coolrl-lost-cities-runs/2026-05-07_eval_device_cuda_1iter`
|
||||
|
||||
CUDA command:
|
||||
|
||||
```bash
|
||||
uv run python -m coolrl_lost_cities.games.classic.deep_cfr.cli train \
|
||||
--config configs/deep_cfr/deep_cfr_selfplay_full_depth_slot_playability.yaml \
|
||||
--checkpoint-dir /mnt/2tbhdd/coolrl-lost-cities-runs/2026-05-07_eval_device_cuda_1iter \
|
||||
--max-iterations 1 \
|
||||
--eval-every 1 \
|
||||
--save-latest-only \
|
||||
--device cuda
|
||||
```
|
||||
|
||||
Top-level timing:
|
||||
|
||||
| Metric | CPU | CUDA | CUDA / CPU |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| `iteration_seconds` | 50.969 | 72.501 | 1.42 |
|
||||
| `evaluation_seconds` | 38.920 | 61.826 | 1.59 |
|
||||
| `traversal_seconds` | 6.343 | 6.409 | 1.01 |
|
||||
| `advantage_train_seconds` | 4.054 | 2.884 | 0.71 |
|
||||
| `strategy_train_seconds` | 1.643 | 1.369 | 0.83 |
|
||||
|
||||
Opponent elapsed timing:
|
||||
|
||||
| Opponent | CPU elapsed | CUDA elapsed | CUDA / CPU |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| `random` | 4.038 | 7.220 | 1.79 |
|
||||
| `passive_discard` | 0.990 | 1.773 | 1.79 |
|
||||
| `safe_heuristic` | 8.701 | 13.518 | 1.55 |
|
||||
| `safe_heuristic_loose` | 8.334 | 13.145 | 1.58 |
|
||||
| `safe_heuristic_strict` | 9.097 | 13.882 | 1.53 |
|
||||
| `noisy_safe` | 7.751 | 12.281 | 1.58 |
|
||||
|
||||
Opponent breakdown for the CPU run:
|
||||
|
||||
| Opponent | elapsed | policy turns | network | network / turn | postprocess | encoding | legal mask | opponent act | avg len |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| `random` | 4.038 | 32170 | 2.401 | 0.075 ms | 0.586 | 0.169 | 0.114 | 0.361 | 644.4 |
|
||||
| `passive_discard` | 0.990 | 8562 | 0.633 | 0.074 ms | 0.155 | 0.044 | 0.034 | 0.010 | 172.2 |
|
||||
| `safe_heuristic` | 8.701 | 49756 | 3.650 | 0.073 ms | 0.903 | 0.253 | 0.211 | 3.068 | 995.1 |
|
||||
| `safe_heuristic_loose` | 8.334 | 48958 | 3.592 | 0.073 ms | 0.886 | 0.248 | 0.207 | 2.798 | 979.2 |
|
||||
| `safe_heuristic_strict` | 9.097 | 50000 | 3.678 | 0.074 ms | 0.910 | 0.254 | 0.214 | 3.420 | 1000.0 |
|
||||
| `noisy_safe` | 7.751 | 47276 | 3.501 | 0.074 ms | 0.864 | 0.244 | 0.195 | 2.354 | 945.7 |
|
||||
| Total | 38.911 | 236722 | 17.456 | 0.074 ms | 4.304 | 1.212 | 0.975 | 12.011 | |
|
||||
|
||||
Opponent breakdown for the CUDA run:
|
||||
|
||||
| Opponent | elapsed | policy turns | network | network / turn | postprocess | encoding | legal mask | opponent act | avg len |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| `random` | 7.220 | 32096 | 5.337 | 0.166 ms | 0.716 | 0.179 | 0.129 | 0.384 | 642.9 |
|
||||
| `passive_discard` | 1.773 | 8562 | 1.364 | 0.159 ms | 0.185 | 0.047 | 0.039 | 0.012 | 172.2 |
|
||||
| `safe_heuristic` | 13.518 | 49876 | 8.053 | 0.161 ms | 1.097 | 0.274 | 0.239 | 3.139 | 997.5 |
|
||||
| `safe_heuristic_loose` | 13.145 | 49078 | 7.967 | 0.162 ms | 1.080 | 0.271 | 0.236 | 2.890 | 981.6 |
|
||||
| `safe_heuristic_strict` | 13.882 | 50000 | 8.034 | 0.161 ms | 1.096 | 0.274 | 0.239 | 3.524 | 1000.0 |
|
||||
| `noisy_safe` | 12.281 | 47266 | 7.650 | 0.162 ms | 1.044 | 0.262 | 0.219 | 2.421 | 945.5 |
|
||||
| Total | 61.819 | 236878 | 38.406 | 0.162 ms | 5.218 | 1.307 | 1.101 | 12.370 | |
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# Deep CFR Legacy Runtime Comparison 2026-05-07
|
||||
|
||||
This note records a runtime summary from the older `../coolrl` Lost Cities
|
||||
Deep CFR implementation and compares it with the current profiling runs in
|
||||
this repository.
|
||||
|
||||
## Legacy Run Summary
|
||||
|
||||
The older run completed metrics through iteration 387. The process had stopped
|
||||
before completing iteration 388.
|
||||
|
||||
Total elapsed time through iteration 387:
|
||||
|
||||
`5879.17s`, or about `1h 37m 59s`.
|
||||
|
||||
| Segment | Mean | Median | Note |
|
||||
| --- | ---: | ---: | --- |
|
||||
| All iterations | 15.19s/iter | 11.44s | Includes eval iterations |
|
||||
| Non-eval iterations | 11.51s/iter | 11.32s | Normal training iteration |
|
||||
| Eval iterations | 30.00s/iter | 29.31s | Eval every 5 iterations |
|
||||
| Evaluation only | 18.61s/eval | 18.04s | Early evals were slower |
|
||||
| Traversal | 7.16s/iter | 6.98s | 140 traversals/iter |
|
||||
| Advantage train | 2.81s/iter | 2.78s | Player 0 + player 1 |
|
||||
| Strategy train | 1.44s/iter | 1.44s | |
|
||||
| Overall throughput | 5387 nodes/s | 5437 nodes/s | |
|
||||
| Traversal throughput | 19.8 traversals/s | 20.1 traversals/s | |
|
||||
|
||||
Recent 50 iteration window from that run:
|
||||
|
||||
| Segment | Mean |
|
||||
| --- | ---: |
|
||||
| All iterations | 15.57s/iter |
|
||||
| Non-eval iterations | 12.21s/iter |
|
||||
| Recent 20 evals, eval only | 17.31s/eval |
|
||||
| Recent 20 eval iterations | 29.30s/iter |
|
||||
|
||||
Evaluation ran every 5 iterations: 5, 10, 15, ..., 385.
|
||||
|
||||
The practical legacy cadence was roughly:
|
||||
|
||||
`4 normal iterations + 1 eval iteration ~= 75s per 5 iterations`.
|
||||
|
||||
## Current Repo Reference Points
|
||||
|
||||
From `docs/deep-cfr-profile-advantage-memory-split-2026-05-07.md`:
|
||||
|
||||
| Segment | Current mean |
|
||||
| --- | ---: |
|
||||
| Non-eval iterations | 5.832958s/iter |
|
||||
| Traversal | 3.160975s/iter |
|
||||
| Advantage train | 1.742895s/iter |
|
||||
| Strategy train | 0.912324s/iter |
|
||||
|
||||
From `docs/deep-cfr-batched-evaluation-2026-05-07.md`:
|
||||
|
||||
| Segment | Current value |
|
||||
| --- | ---: |
|
||||
| Batched CUDA evaluation | 14.834096s/eval |
|
||||
| Batched CUDA 1-iter wall time with eval | 21.322356s |
|
||||
| Batched opponent-parallel CUDA evaluation | 6.420402s/eval |
|
||||
| Batched opponent-parallel CUDA 1-iter wall time with eval | 12.854383s |
|
||||
|
||||
## Rough Comparison
|
||||
|
||||
Normal training iterations improved from about `11.51s` to about `5.83s`,
|
||||
roughly `1.97x` faster.
|
||||
|
||||
Traversal improved from about `7.16s` to about `3.16s`, roughly `2.27x`
|
||||
faster.
|
||||
|
||||
Evaluation improved from about `18.61s` to about `14.83s`, roughly `1.25x`
|
||||
faster for the measured batched CUDA profile. With opponent-parallel eval, the
|
||||
measured eval time was about `6.42s`, roughly `2.90x` faster than the legacy
|
||||
eval-only average.
|
||||
|
||||
Using the simple cadence model:
|
||||
|
||||
Legacy:
|
||||
|
||||
`4 * 11.51 + (11.51 + 18.61) = 76.16s per 5 iterations`
|
||||
|
||||
Current batched sequential:
|
||||
|
||||
`4 * 5.83 + (5.83 + 14.83) = 43.98s per 5 iterations`
|
||||
|
||||
Current batched opponent-parallel:
|
||||
|
||||
`4 * 5.83 + (5.83 + 6.42) = 35.57s per 5 iterations`
|
||||
|
||||
That implies about `1.73x` faster eval-included wall time for the batched
|
||||
sequential rough comparison, and about `2.14x` faster for the batched
|
||||
opponent-parallel rough comparison.
|
||||
|
||||
## Caveat
|
||||
|
||||
The legacy numbers came from a long run through iteration 387. The current
|
||||
numbers are from targeted profiling runs. The comparison is useful for order of
|
||||
magnitude and bottleneck direction, not as a strict benchmark under identical
|
||||
runtime conditions.
|
||||
Reference in New Issue
Block a user