Measure traversal policy boundary cost
Add a microbench that separates game/encoding overhead, single-request policy boundary overhead, and batched PyTorch forward lower bounds. Record CPU/CUDA results and link the finding from the Julia port evaluation. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
@@ -700,6 +700,56 @@ either:
|
|||||||
Both require restructuring traversal. Option A's "additive, no traversal
|
Both require restructuring traversal. Option A's "additive, no traversal
|
||||||
changes" property turned out to also mean "cannot drive the batch size up."
|
changes" property turned out to also mean "cannot drive the batch size up."
|
||||||
|
|
||||||
|
### Clarifying the traversal bottleneck: sync policy boundary, not SIMD
|
||||||
|
|
||||||
|
The tempting shorthand is "Python/GIL prevents traversal from using SIMD or
|
||||||
|
threads." The more precise diagnosis is narrower:
|
||||||
|
|
||||||
|
- Lost Cities game mechanics are already mostly Cython C-level operations.
|
||||||
|
`legal_actions`, action push/pop, and cached scoring are not Python list
|
||||||
|
walks on the hot path.
|
||||||
|
- The traversal recursion is Cython, but it synchronously crosses back into
|
||||||
|
Python/PyTorch at every policy-needed state: encode a single info state,
|
||||||
|
run one-row PyTorch forward, copy logits back to CPU/Numpy, then continue
|
||||||
|
recursion.
|
||||||
|
- This boundary makes every traversal worker **sync-blocking**. With
|
||||||
|
`num_workers=8`, the inference server can see at most eight in-flight
|
||||||
|
requests before per-network splitting, no matter how large `max_batch` is.
|
||||||
|
- GIL-free threading would help only after the same path is made
|
||||||
|
`nogil`-clean or after traversal is restructured so policy calls can be
|
||||||
|
batched. Simply "using SIMD" does not address the one-row policy boundary.
|
||||||
|
|
||||||
|
So the actionable bottleneck is **policy-call scheduling shape**, not scalar
|
||||||
|
game-rule arithmetic. The highest-leverage experiment is Option B:
|
||||||
|
per-worker interleaved traversal, where one worker advances many traversals,
|
||||||
|
suspends each at a policy request, batches those requests, and resumes the
|
||||||
|
corresponding continuations.
|
||||||
|
|
||||||
|
Microbench evidence (2026-05-07, `configs/deep_cfr/default.yaml`,
|
||||||
|
`experiments/traversal_policy_boundary/bench_policy_boundary.py`):
|
||||||
|
|
||||||
|
| Device | Component | median μs/call | p95 μs/call |
|
||||||
|
| --- | --- | ---: | ---: |
|
||||||
|
| CPU | encode + legal | 3.10 | 3.81 |
|
||||||
|
| CPU | push + pop | 0.15 | 0.22 |
|
||||||
|
| CPU | policy boundary bs=1 | 111.50 | 125.46 |
|
||||||
|
| CPU | torch forward bs=64 | 12.84 | 13.14 |
|
||||||
|
| CUDA | policy boundary bs=1 | 181.30 | 194.77 |
|
||||||
|
| CUDA | torch forward bs=64 | 2.55 | 2.75 |
|
||||||
|
|
||||||
|
This confirms the bottleneck is not Cython game-rule scalar work. The
|
||||||
|
single-request policy boundary is ~36× larger than encode+legal on CPU, while
|
||||||
|
CUDA bs=64 forward is ~71× cheaper than the current CUDA bs=1 boundary.
|
||||||
|
|
||||||
|
Expected upside is bounded by the fraction of traversal currently spent at
|
||||||
|
policy calls. Moving realized GPU forward from the current ~4-8 row regime
|
||||||
|
(~12-20μs/state) to bs=64 (~1.46μs/state) is an ~8-14× improvement on the
|
||||||
|
forward component, but not on game recursion, sample creation, or replay
|
||||||
|
writes. For the observed `local` traversal around 10-13s/iter, a realistic
|
||||||
|
first target is roughly **1.5-3× traversal speedup** if Option B reaches the
|
||||||
|
bs=64 regime without adding comparable scheduler overhead. Larger claims need
|
||||||
|
a prototype because traversal has substantial non-forward work.
|
||||||
|
|
||||||
### Why deferring A (not deleting) is the right call
|
### Why deferring A (not deleting) is the right call
|
||||||
|
|
||||||
- The plumbing (server process, shared-memory client, weight sync, config
|
- The plumbing (server process, shared-memory client, weight sync, config
|
||||||
|
|||||||
@@ -226,6 +226,9 @@ Recommended next path: stay on Python/Cython and pursue Option B
|
|||||||
Julia experiment could still be considered later for traversal-only
|
Julia experiment could still be considered later for traversal-only
|
||||||
logic, but it would need an explicit hybrid plan that keeps PyTorch for
|
logic, but it would need an explicit hybrid plan that keeps PyTorch for
|
||||||
networks and separately proves PythonCall/PyCall overhead is acceptable.
|
networks and separately proves PythonCall/PyCall overhead is acceptable.
|
||||||
|
For the traversal bottleneck clarification and Option B speedup envelope,
|
||||||
|
see `docs/performance.md` "Clarifying the traversal bottleneck: sync
|
||||||
|
policy boundary, not SIMD."
|
||||||
|
|
||||||
Criterion 5 remains unrun because criterion 4 already blocks the full
|
Criterion 5 remains unrun because criterion 4 already blocks the full
|
||||||
port decision.
|
port decision.
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Traversal Policy Boundary Microbench
|
||||||
|
|
||||||
|
Purpose: separate the current traversal hot path into game/encoding overhead,
|
||||||
|
single-request policy boundary overhead, and batched forward lower bounds.
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run python experiments/traversal_policy_boundary/bench_policy_boundary.py \
|
||||||
|
--traversals 32 \
|
||||||
|
--runs 5 \
|
||||||
|
--warmup 1 \
|
||||||
|
--corpus-size 512 \
|
||||||
|
--component-repeats 4 \
|
||||||
|
--forward-repeats 32 \
|
||||||
|
--device cpu
|
||||||
|
```
|
||||||
|
|
||||||
|
CUDA spot check:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run python experiments/traversal_policy_boundary/bench_policy_boundary.py \
|
||||||
|
--traversals 8 \
|
||||||
|
--runs 3 \
|
||||||
|
--warmup 1 \
|
||||||
|
--corpus-size 512 \
|
||||||
|
--component-repeats 2 \
|
||||||
|
--forward-repeats 32 \
|
||||||
|
--device cuda \
|
||||||
|
--output experiments/traversal_policy_boundary/results_cuda.json
|
||||||
|
```
|
||||||
|
|
||||||
|
2026-05-07 results, `configs/deep_cfr/default.yaml`, RTX 3090 host:
|
||||||
|
|
||||||
|
| Device | Component | Median us/call | p95 us/call |
|
||||||
|
| --- | --- | ---: | ---: |
|
||||||
|
| CPU | encode + legal | 3.10 | 3.81 |
|
||||||
|
| CPU | push + pop | 0.15 | 0.22 |
|
||||||
|
| CPU | policy boundary bs=1 | 111.50 | 125.46 |
|
||||||
|
| CPU | torch forward bs=64 | 12.84 | 13.14 |
|
||||||
|
| CUDA | encode + legal | 3.16 | 3.88 |
|
||||||
|
| CUDA | push + pop | 0.16 | 0.25 |
|
||||||
|
| CUDA | policy boundary bs=1 | 181.30 | 194.77 |
|
||||||
|
| CUDA | torch forward bs=64 | 2.55 | 2.75 |
|
||||||
|
|
||||||
|
Interpretation: the game mechanics and state encoding are not the dominant
|
||||||
|
cost. The current one-row policy boundary dominates, and CUDA only becomes
|
||||||
|
attractive once requests are actually batched.
|
||||||
@@ -0,0 +1,401 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import random
|
||||||
|
import statistics
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
from coolrl_lost_cities.games.classic.deep_cfr.encoding import encode_info_state, input_dim
|
||||||
|
from coolrl_lost_cities.games.classic.deep_cfr.traversal import run_cython_traversal_batch
|
||||||
|
from coolrl_lost_cities.games.classic.game import GameState
|
||||||
|
|
||||||
|
from coolrl_lost_cities.games.classic.deep_cfr.config import load_config
|
||||||
|
from coolrl_lost_cities.games.classic.deep_cfr.networks import DeepCFRMLP
|
||||||
|
|
||||||
|
|
||||||
|
def _timer(device: torch.device | None = None) -> float:
|
||||||
|
if device is not None and device.type == "cuda":
|
||||||
|
torch.cuda.synchronize(device)
|
||||||
|
return time.perf_counter()
|
||||||
|
|
||||||
|
|
||||||
|
def _median(values: list[float]) -> float:
|
||||||
|
return float(statistics.median(values))
|
||||||
|
|
||||||
|
|
||||||
|
def _quantile(values: list[float], q: float) -> float:
|
||||||
|
ordered = sorted(values)
|
||||||
|
if not ordered:
|
||||||
|
return 0.0
|
||||||
|
idx = min(len(ordered) - 1, max(0, round((len(ordered) - 1) * q)))
|
||||||
|
return float(ordered[idx])
|
||||||
|
|
||||||
|
|
||||||
|
def _build_networks(cfg: Any, input_dim_value: int, action_size: int, device: torch.device):
|
||||||
|
networks = [
|
||||||
|
DeepCFRMLP.from_config(input_dim_value, action_size, cfg.network).to(device).eval()
|
||||||
|
for _ in range(2)
|
||||||
|
]
|
||||||
|
strategy = DeepCFRMLP.from_config(input_dim_value, action_size, cfg.network).to(device).eval()
|
||||||
|
return networks, strategy
|
||||||
|
|
||||||
|
|
||||||
|
def _make_corpus(cfg: Any, count: int, seed: int) -> list[GameState]:
|
||||||
|
rng = random.Random(seed)
|
||||||
|
game_config = cfg.rules.to_lost_cities_config(seed=seed)
|
||||||
|
states: list[GameState] = []
|
||||||
|
state = GameState.new_game(game_config, seed=seed)
|
||||||
|
while len(states) < count:
|
||||||
|
if state.terminal:
|
||||||
|
state = GameState.new_game(game_config, seed=rng.randrange(1, 2**31))
|
||||||
|
continue
|
||||||
|
states.append(state.clone())
|
||||||
|
legal = state.unified_legal_actions()
|
||||||
|
if not legal:
|
||||||
|
state = GameState.new_game(game_config, seed=rng.randrange(1, 2**31))
|
||||||
|
continue
|
||||||
|
state.push_unified_action(int(legal[rng.randrange(len(legal))]))
|
||||||
|
return states
|
||||||
|
|
||||||
|
|
||||||
|
def _regret_matching(values: np.ndarray, legal: list[int], epsilon: float) -> np.ndarray:
|
||||||
|
policy = np.zeros_like(values, dtype=np.float32)
|
||||||
|
positives = [max(float(values[action]), 0.0) for action in legal]
|
||||||
|
positive_sum = sum(positives)
|
||||||
|
if positive_sum <= epsilon:
|
||||||
|
uniform = 1.0 / max(1, len(legal))
|
||||||
|
for action in legal:
|
||||||
|
policy[action] = uniform
|
||||||
|
return policy
|
||||||
|
for action, positive in zip(legal, positives, strict=True):
|
||||||
|
policy[action] = positive / positive_sum
|
||||||
|
return policy
|
||||||
|
|
||||||
|
|
||||||
|
def bench_full_traversal(
|
||||||
|
cfg: Any,
|
||||||
|
*,
|
||||||
|
traversals: int,
|
||||||
|
runs: int,
|
||||||
|
warmup: int,
|
||||||
|
device: torch.device,
|
||||||
|
input_dim_value: int,
|
||||||
|
action_size: int,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
timings: list[float] = []
|
||||||
|
stat_rows: list[dict[str, Any]] = []
|
||||||
|
seed_base = int(cfg.run.seed)
|
||||||
|
game_config = cfg.rules.to_lost_cities_config(seed=seed_base)
|
||||||
|
|
||||||
|
for run_idx in range(runs + warmup):
|
||||||
|
networks, strategy = _build_networks(cfg, input_dim_value, action_size, device)
|
||||||
|
seeds = [seed_base + run_idx * 100_000 + idx for idx in range(traversals)]
|
||||||
|
start = _timer(device)
|
||||||
|
stats, advantage_samples, strategy_samples = run_cython_traversal_batch(
|
||||||
|
networks,
|
||||||
|
game_config,
|
||||||
|
seeds,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
device=device,
|
||||||
|
action_size=action_size,
|
||||||
|
strategy_network=strategy,
|
||||||
|
encoding=cfg.encoding,
|
||||||
|
epsilon=cfg.traversal.regret_matching_epsilon,
|
||||||
|
strategy_sample_interval=cfg.traversal.strategy_sample_interval,
|
||||||
|
store_strategy_on_traverser_nodes=cfg.traversal.store_strategy_on_traverser_nodes,
|
||||||
|
store_strategy_on_opponent_nodes=cfg.traversal.store_strategy_on_opponent_nodes,
|
||||||
|
max_depth=cfg.traversal.max_depth,
|
||||||
|
max_nodes=cfg.traversal.max_nodes_per_traversal,
|
||||||
|
sampling_mode=cfg.traversal.sampling_mode,
|
||||||
|
outcome_sampling_epsilon=cfg.traversal.outcome_sampling_epsilon,
|
||||||
|
outcome_sampling_value_clip=cfg.traversal.outcome_sampling_value_clip,
|
||||||
|
outcome_unsampled_regret=cfg.traversal.outcome_unsampled_regret,
|
||||||
|
cutoff_value_mode=cfg.traversal.cutoff_value_mode,
|
||||||
|
cutoff_rollouts=cfg.traversal.cutoff_rollouts,
|
||||||
|
cutoff_rollout_policy=cfg.traversal.cutoff_rollout_policy,
|
||||||
|
cutoff_rollout_max_steps=cfg.traversal.cutoff_rollout_max_steps,
|
||||||
|
opponent_policy=cfg.traversal.opponent_policy,
|
||||||
|
all_negative_fallback=cfg.regret_matching.all_negative_fallback,
|
||||||
|
league_advantage_networks=None,
|
||||||
|
self_play_anchor_probability=cfg.self_play.anchor_probability,
|
||||||
|
self_play_current_weight=cfg.self_play.current_weight,
|
||||||
|
self_play_recent_weight=cfg.self_play.recent_weight,
|
||||||
|
self_play_older_weight=cfg.self_play.older_weight,
|
||||||
|
self_play_anchor_weight=cfg.self_play.anchor_weight,
|
||||||
|
self_play_recent_window=cfg.self_play.recent_window,
|
||||||
|
endpoint_depth_bucket_width=cfg.traversal.endpoint_depth_bucket_width,
|
||||||
|
endpoint_depth_bucket_max=cfg.traversal.endpoint_depth_bucket_max,
|
||||||
|
seed=seed_base + run_idx,
|
||||||
|
)
|
||||||
|
elapsed = _timer(device) - start
|
||||||
|
if run_idx >= warmup:
|
||||||
|
stats_dict = stats.to_dict()
|
||||||
|
stats_dict["advantage_samples_returned"] = len(advantage_samples)
|
||||||
|
stats_dict["strategy_samples_returned"] = len(strategy_samples)
|
||||||
|
timings.append(elapsed)
|
||||||
|
stat_rows.append(stats_dict)
|
||||||
|
del networks, strategy
|
||||||
|
|
||||||
|
policy_calls = [int(row["traversal_regret_matching_decisions"]) for row in stat_rows]
|
||||||
|
nodes = [int(row["traversal_nodes"]) for row in stat_rows]
|
||||||
|
return {
|
||||||
|
"seconds": timings,
|
||||||
|
"median_seconds": _median(timings),
|
||||||
|
"median_nodes": _median([float(value) for value in nodes]),
|
||||||
|
"median_policy_calls": _median([float(value) for value in policy_calls]),
|
||||||
|
"median_us_per_node": _median(
|
||||||
|
[sec * 1_000_000.0 / max(1, node) for sec, node in zip(timings, nodes, strict=True)]
|
||||||
|
),
|
||||||
|
"median_us_per_policy_call": _median(
|
||||||
|
[
|
||||||
|
sec * 1_000_000.0 / max(1, calls)
|
||||||
|
for sec, calls in zip(timings, policy_calls, strict=True)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"stats": stat_rows,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def bench_encode_legal(corpus: list[GameState], cfg: Any, repeats: int) -> dict[str, Any]:
|
||||||
|
durations: list[float] = []
|
||||||
|
counts: list[int] = []
|
||||||
|
total = 0.0
|
||||||
|
for _ in range(repeats):
|
||||||
|
for state in corpus:
|
||||||
|
start = time.perf_counter()
|
||||||
|
_ = encode_info_state(state, state.current_player, cfg.encoding)
|
||||||
|
legal = state.unified_legal_actions()
|
||||||
|
total += len(legal)
|
||||||
|
durations.append(time.perf_counter() - start)
|
||||||
|
counts.append(len(legal))
|
||||||
|
return {
|
||||||
|
"median_us": _median(durations) * 1_000_000.0,
|
||||||
|
"p95_us": _quantile(durations, 0.95) * 1_000_000.0,
|
||||||
|
"mean_legal_actions": float(statistics.mean(counts)),
|
||||||
|
"checksum": total,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def bench_push_pop(corpus: list[GameState], repeats: int) -> dict[str, Any]:
|
||||||
|
durations: list[float] = []
|
||||||
|
total = 0
|
||||||
|
for _ in range(repeats):
|
||||||
|
for state in corpus:
|
||||||
|
legal = state.unified_legal_actions()
|
||||||
|
if not legal:
|
||||||
|
continue
|
||||||
|
action = int(legal[total % len(legal)])
|
||||||
|
start = time.perf_counter()
|
||||||
|
state.push_unified_action(action)
|
||||||
|
state.pop_action()
|
||||||
|
durations.append(time.perf_counter() - start)
|
||||||
|
total += action
|
||||||
|
return {
|
||||||
|
"median_us": _median(durations) * 1_000_000.0,
|
||||||
|
"p95_us": _quantile(durations, 0.95) * 1_000_000.0,
|
||||||
|
"checksum": total,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def bench_policy_boundary(
|
||||||
|
corpus: list[GameState],
|
||||||
|
cfg: Any,
|
||||||
|
network: torch.nn.Module,
|
||||||
|
device: torch.device,
|
||||||
|
action_size: int,
|
||||||
|
repeats: int,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
durations: list[float] = []
|
||||||
|
checksum = 0.0
|
||||||
|
with torch.inference_mode():
|
||||||
|
for _ in range(repeats):
|
||||||
|
for state in corpus:
|
||||||
|
start = _timer(device)
|
||||||
|
info_state = encode_info_state(state, state.current_player, cfg.encoding)
|
||||||
|
legal = state.unified_legal_actions()
|
||||||
|
x = torch.as_tensor(info_state, dtype=torch.float32, device=device).unsqueeze(0)
|
||||||
|
advantages = network(x).squeeze(0).detach().cpu().numpy().astype(np.float32)
|
||||||
|
policy = _regret_matching(
|
||||||
|
advantages,
|
||||||
|
legal,
|
||||||
|
float(cfg.traversal.regret_matching_epsilon),
|
||||||
|
)
|
||||||
|
checksum += float(policy.sum()) + float(advantages[0])
|
||||||
|
durations.append(_timer(device) - start)
|
||||||
|
return {
|
||||||
|
"median_us": _median(durations) * 1_000_000.0,
|
||||||
|
"p95_us": _quantile(durations, 0.95) * 1_000_000.0,
|
||||||
|
"checksum": checksum,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def bench_torch_forward(
|
||||||
|
corpus: list[GameState],
|
||||||
|
cfg: Any,
|
||||||
|
network: torch.nn.Module,
|
||||||
|
device: torch.device,
|
||||||
|
batch_sizes: list[int],
|
||||||
|
repeats: int,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
states = np.stack(
|
||||||
|
[encode_info_state(state, state.current_player, cfg.encoding) for state in corpus]
|
||||||
|
).astype(np.float32)
|
||||||
|
results: dict[str, Any] = {}
|
||||||
|
with torch.inference_mode():
|
||||||
|
for batch_size in batch_sizes:
|
||||||
|
durations: list[float] = []
|
||||||
|
checksum = 0.0
|
||||||
|
for _ in range(repeats):
|
||||||
|
for offset in range(0, len(states), batch_size):
|
||||||
|
chunk = states[offset : offset + batch_size]
|
||||||
|
if len(chunk) != batch_size:
|
||||||
|
continue
|
||||||
|
x = torch.as_tensor(chunk, dtype=torch.float32, device=device)
|
||||||
|
start = _timer(device)
|
||||||
|
out = network(x)
|
||||||
|
checksum += float(out.detach().sum().cpu())
|
||||||
|
durations.append((_timer(device) - start) / batch_size)
|
||||||
|
if not durations:
|
||||||
|
continue
|
||||||
|
results[str(batch_size)] = {
|
||||||
|
"median_us_per_call": _median(durations) * 1_000_000.0,
|
||||||
|
"p95_us_per_call": _quantile(durations, 0.95) * 1_000_000.0,
|
||||||
|
"checksum": checksum,
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def _print_table(result: dict[str, Any]) -> None:
|
||||||
|
full = result["full_traversal"]
|
||||||
|
encode = result["encode_legal"]
|
||||||
|
push_pop = result["push_pop"]
|
||||||
|
boundary = result["policy_boundary_bs1"]
|
||||||
|
forward = result["torch_forward"]
|
||||||
|
|
||||||
|
print("Traversal policy-boundary microbench")
|
||||||
|
print(
|
||||||
|
f"config={result['config']} device={result['device']} torch_threads={result['torch_threads']}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"full traversal: {full['median_seconds']:.3f}s, "
|
||||||
|
f"policy_calls={full['median_policy_calls']:.0f}, nodes={full['median_nodes']:.0f}"
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
print("component median us/call p95 us/call")
|
||||||
|
print(
|
||||||
|
f"encode+legal {encode['median_us']:9.2f} {encode['p95_us']:9.2f}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"push+pop {push_pop['median_us']:9.2f} {push_pop['p95_us']:9.2f}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"policy boundary bs=1 {boundary['median_us']:9.2f} {boundary['p95_us']:9.2f}"
|
||||||
|
)
|
||||||
|
for batch_size, row in forward.items():
|
||||||
|
print(
|
||||||
|
f"torch forward bs={batch_size:<3} "
|
||||||
|
f"{row['median_us_per_call']:9.2f} {row['p95_us_per_call']:9.2f}"
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
print("derived")
|
||||||
|
print(f"full traversal us/node {full['median_us_per_node']:.2f}")
|
||||||
|
print(f"full traversal us/policy_call {full['median_us_per_policy_call']:.2f}")
|
||||||
|
if boundary["median_us"] > 0:
|
||||||
|
print(
|
||||||
|
"full/policy-boundary ratio "
|
||||||
|
f"{full['median_us_per_policy_call'] / boundary['median_us']:.2f}x"
|
||||||
|
)
|
||||||
|
bs64 = forward.get("64")
|
||||||
|
if bs64 and bs64["median_us_per_call"] > 0:
|
||||||
|
print(
|
||||||
|
"policy bs=1 vs forward bs=64 "
|
||||||
|
f"{boundary['median_us'] / bs64['median_us_per_call']:.2f}x"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--config", default="configs/deep_cfr/default.yaml")
|
||||||
|
parser.add_argument("--device", default="cpu")
|
||||||
|
parser.add_argument("--traversals", type=int, default=32)
|
||||||
|
parser.add_argument("--runs", type=int, default=5)
|
||||||
|
parser.add_argument("--warmup", type=int, default=1)
|
||||||
|
parser.add_argument("--corpus-size", type=int, default=512)
|
||||||
|
parser.add_argument("--component-repeats", type=int, default=4)
|
||||||
|
parser.add_argument("--forward-repeats", type=int, default=64)
|
||||||
|
parser.add_argument("--torch-threads", type=int, default=1)
|
||||||
|
parser.add_argument("--output", default="")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
torch.set_num_threads(max(1, int(args.torch_threads)))
|
||||||
|
cfg = load_config(args.config)
|
||||||
|
device = torch.device(args.device)
|
||||||
|
if device.type == "cuda" and not torch.cuda.is_available():
|
||||||
|
raise RuntimeError("--device cuda requested but CUDA is not available")
|
||||||
|
|
||||||
|
probe = GameState.new_game(
|
||||||
|
cfg.rules.to_lost_cities_config(seed=cfg.run.seed), seed=cfg.run.seed
|
||||||
|
)
|
||||||
|
input_dim_value = input_dim(probe, cfg.encoding)
|
||||||
|
action_size = 2 * probe.config.hand_size + 1 + probe.config.n_colors
|
||||||
|
networks, _strategy = _build_networks(cfg, input_dim_value, action_size, device)
|
||||||
|
corpus = _make_corpus(cfg, args.corpus_size, int(cfg.run.seed))
|
||||||
|
|
||||||
|
# Run a tiny warmup through the component path before timed loops.
|
||||||
|
_ = encode_info_state(corpus[0], corpus[0].current_player, cfg.encoding)
|
||||||
|
with torch.inference_mode():
|
||||||
|
_ = networks[0](torch.as_tensor(_, dtype=torch.float32, device=device).unsqueeze(0))
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"config": args.config,
|
||||||
|
"device": str(device),
|
||||||
|
"torch_threads": torch.get_num_threads(),
|
||||||
|
"traversals": args.traversals,
|
||||||
|
"runs": args.runs,
|
||||||
|
"warmup": args.warmup,
|
||||||
|
"corpus_size": args.corpus_size,
|
||||||
|
"input_dim": input_dim_value,
|
||||||
|
"action_size": action_size,
|
||||||
|
"full_traversal": bench_full_traversal(
|
||||||
|
cfg,
|
||||||
|
traversals=args.traversals,
|
||||||
|
runs=args.runs,
|
||||||
|
warmup=args.warmup,
|
||||||
|
device=device,
|
||||||
|
input_dim_value=input_dim_value,
|
||||||
|
action_size=action_size,
|
||||||
|
),
|
||||||
|
"encode_legal": bench_encode_legal(corpus, cfg, args.component_repeats),
|
||||||
|
"push_pop": bench_push_pop(corpus, args.component_repeats),
|
||||||
|
"policy_boundary_bs1": bench_policy_boundary(
|
||||||
|
corpus,
|
||||||
|
cfg,
|
||||||
|
networks[0],
|
||||||
|
device,
|
||||||
|
action_size,
|
||||||
|
args.component_repeats,
|
||||||
|
),
|
||||||
|
"torch_forward": bench_torch_forward(
|
||||||
|
corpus,
|
||||||
|
cfg,
|
||||||
|
networks[0],
|
||||||
|
device,
|
||||||
|
[1, 4, 8, 64, 256],
|
||||||
|
args.forward_repeats,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
_print_table(result)
|
||||||
|
|
||||||
|
output = Path(args.output) if args.output else Path(__file__).with_name("results.json")
|
||||||
|
output.write_text(json.dumps(result, indent=2), encoding="utf-8")
|
||||||
|
print(f"\nwrote {output}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,359 @@
|
|||||||
|
{
|
||||||
|
"config": "configs/deep_cfr/default.yaml",
|
||||||
|
"device": "cpu",
|
||||||
|
"torch_threads": 1,
|
||||||
|
"traversals": 32,
|
||||||
|
"runs": 5,
|
||||||
|
"warmup": 1,
|
||||||
|
"corpus_size": 512,
|
||||||
|
"input_dim": 365,
|
||||||
|
"action_size": 22,
|
||||||
|
"full_traversal": {
|
||||||
|
"seconds": [
|
||||||
|
1.4774325820035301,
|
||||||
|
1.3015301960112993,
|
||||||
|
0.8726662800181657,
|
||||||
|
1.3711390359967481,
|
||||||
|
1.4644392740156036
|
||||||
|
],
|
||||||
|
"median_seconds": 1.3711390359967481,
|
||||||
|
"median_nodes": 12960.0,
|
||||||
|
"median_policy_calls": 6470.0,
|
||||||
|
"median_us_per_node": 107.45790918191044,
|
||||||
|
"median_us_per_policy_call": 215.2712861414653,
|
||||||
|
"stats": [
|
||||||
|
{
|
||||||
|
"traversal_nodes": 13710,
|
||||||
|
"traversal_terminals": 32,
|
||||||
|
"traversal_depth_cutoffs": 0,
|
||||||
|
"traversal_node_limit_cutoffs": 0,
|
||||||
|
"traversal_max_depth_reached": 560,
|
||||||
|
"traversal_advantage_samples": 6842,
|
||||||
|
"traversal_strategy_samples": 6842,
|
||||||
|
"traversal_sampled_actions": 6842,
|
||||||
|
"traversal_regret_matching_decisions": 6842,
|
||||||
|
"traversal_regret_fallback_count": 512,
|
||||||
|
"traversal_regret_fallback_rate": 0.07483192049108447,
|
||||||
|
"traversal_regret_fallback_avg_depth": 91.796875,
|
||||||
|
"traversal_regret_fallback_action_play_existing": 0,
|
||||||
|
"traversal_regret_fallback_action_open_new": 0,
|
||||||
|
"traversal_regret_fallback_action_discard": 0,
|
||||||
|
"traversal_regret_fallback_action_draw_deck": 208,
|
||||||
|
"traversal_regret_fallback_action_draw_pile": 304,
|
||||||
|
"traversal_regret_fallback_legal_actions_mean": 2.046875,
|
||||||
|
"traversal_regret_fallback_legal_play_existing_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_open_new_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_discard_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_deck_mean": 1.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_pile_mean": 1.046875,
|
||||||
|
"traversal_regret_fallback_open_new_available_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selected": 0,
|
||||||
|
"traversal_regret_fallback_open_new_selected_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selection_over_availability": 0.0,
|
||||||
|
"traversal_regret_fallback_avg_opened_colors_before_action": 3.384765625,
|
||||||
|
"traversal_regret_fallback_argmax_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_size_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_rate": 0.0,
|
||||||
|
"traversal_cutoff_rollouts": 0,
|
||||||
|
"traversal_cutoff_rollout_steps": 0,
|
||||||
|
"traversal_cutoff_rollout_timeouts": 0,
|
||||||
|
"traversal_endpoint_depth_sum": 13678,
|
||||||
|
"traversal_endpoints": 32,
|
||||||
|
"traversal_avg_endpoint_depth": 427.4375,
|
||||||
|
"traversal_endpoint_depth_bucket_400_499": 15,
|
||||||
|
"traversal_endpoint_depth_bucket_500_599": 5,
|
||||||
|
"traversal_endpoint_depth_bucket_300_399": 12,
|
||||||
|
"traversal_regret_fallback_depth_bucket_200_249": 26,
|
||||||
|
"traversal_regret_fallback_depth_bucket_150_199": 53,
|
||||||
|
"traversal_regret_fallback_depth_bucket_50_99": 109,
|
||||||
|
"traversal_regret_fallback_depth_bucket_0_49": 212,
|
||||||
|
"traversal_regret_fallback_depth_bucket_400_plus": 3,
|
||||||
|
"traversal_regret_fallback_depth_bucket_250_299": 18,
|
||||||
|
"traversal_regret_fallback_depth_bucket_100_149": 82,
|
||||||
|
"traversal_regret_fallback_depth_bucket_300_349": 8,
|
||||||
|
"traversal_regret_fallback_depth_bucket_350_399": 1,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_5": 152,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_4": 128,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_3": 98,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_2": 64,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_1": 39,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_0": 31,
|
||||||
|
"advantage_samples_returned": 6842,
|
||||||
|
"strategy_samples_returned": 6842
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traversal_nodes": 12112,
|
||||||
|
"traversal_terminals": 32,
|
||||||
|
"traversal_depth_cutoffs": 0,
|
||||||
|
"traversal_node_limit_cutoffs": 0,
|
||||||
|
"traversal_max_depth_reached": 504,
|
||||||
|
"traversal_advantage_samples": 6046,
|
||||||
|
"traversal_strategy_samples": 6046,
|
||||||
|
"traversal_sampled_actions": 6046,
|
||||||
|
"traversal_regret_matching_decisions": 6046,
|
||||||
|
"traversal_regret_fallback_count": 680,
|
||||||
|
"traversal_regret_fallback_rate": 0.11247105524313596,
|
||||||
|
"traversal_regret_fallback_avg_depth": 98.55294117647058,
|
||||||
|
"traversal_regret_fallback_action_play_existing": 0,
|
||||||
|
"traversal_regret_fallback_action_open_new": 0,
|
||||||
|
"traversal_regret_fallback_action_discard": 0,
|
||||||
|
"traversal_regret_fallback_action_draw_deck": 366,
|
||||||
|
"traversal_regret_fallback_action_draw_pile": 314,
|
||||||
|
"traversal_regret_fallback_legal_actions_mean": 2.6470588235294117,
|
||||||
|
"traversal_regret_fallback_legal_play_existing_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_open_new_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_discard_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_deck_mean": 1.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_pile_mean": 1.6470588235294117,
|
||||||
|
"traversal_regret_fallback_open_new_available_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selected": 0,
|
||||||
|
"traversal_regret_fallback_open_new_selected_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selection_over_availability": 0.0,
|
||||||
|
"traversal_regret_fallback_avg_opened_colors_before_action": 4.067647058823529,
|
||||||
|
"traversal_regret_fallback_argmax_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_size_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_rate": 0.0,
|
||||||
|
"traversal_cutoff_rollouts": 0,
|
||||||
|
"traversal_cutoff_rollout_steps": 0,
|
||||||
|
"traversal_cutoff_rollout_timeouts": 0,
|
||||||
|
"traversal_endpoint_depth_sum": 12080,
|
||||||
|
"traversal_endpoints": 32,
|
||||||
|
"traversal_avg_endpoint_depth": 377.5,
|
||||||
|
"traversal_endpoint_depth_bucket_400_499": 11,
|
||||||
|
"traversal_endpoint_depth_bucket_200_299": 3,
|
||||||
|
"traversal_endpoint_depth_bucket_300_399": 17,
|
||||||
|
"traversal_endpoint_depth_bucket_500_599": 1,
|
||||||
|
"traversal_regret_fallback_depth_bucket_300_349": 17,
|
||||||
|
"traversal_regret_fallback_depth_bucket_200_249": 46,
|
||||||
|
"traversal_regret_fallback_depth_bucket_150_199": 74,
|
||||||
|
"traversal_regret_fallback_depth_bucket_100_149": 108,
|
||||||
|
"traversal_regret_fallback_depth_bucket_50_99": 147,
|
||||||
|
"traversal_regret_fallback_depth_bucket_0_49": 258,
|
||||||
|
"traversal_regret_fallback_depth_bucket_250_299": 24,
|
||||||
|
"traversal_regret_fallback_depth_bucket_350_399": 6,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_5": 404,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_4": 107,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_3": 57,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_2": 56,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_1": 35,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_0": 21,
|
||||||
|
"advantage_samples_returned": 6046,
|
||||||
|
"strategy_samples_returned": 6046
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traversal_nodes": 8094,
|
||||||
|
"traversal_terminals": 32,
|
||||||
|
"traversal_depth_cutoffs": 0,
|
||||||
|
"traversal_node_limit_cutoffs": 0,
|
||||||
|
"traversal_max_depth_reached": 342,
|
||||||
|
"traversal_advantage_samples": 4048,
|
||||||
|
"traversal_strategy_samples": 4048,
|
||||||
|
"traversal_sampled_actions": 4048,
|
||||||
|
"traversal_regret_matching_decisions": 4048,
|
||||||
|
"traversal_regret_fallback_count": 0,
|
||||||
|
"traversal_regret_fallback_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_avg_depth": 0.0,
|
||||||
|
"traversal_regret_fallback_action_play_existing": 0,
|
||||||
|
"traversal_regret_fallback_action_open_new": 0,
|
||||||
|
"traversal_regret_fallback_action_discard": 0,
|
||||||
|
"traversal_regret_fallback_action_draw_deck": 0,
|
||||||
|
"traversal_regret_fallback_action_draw_pile": 0,
|
||||||
|
"traversal_regret_fallback_legal_actions_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_play_existing_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_open_new_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_discard_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_deck_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_pile_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_available_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selected": 0,
|
||||||
|
"traversal_regret_fallback_open_new_selected_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selection_over_availability": 0.0,
|
||||||
|
"traversal_regret_fallback_avg_opened_colors_before_action": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_size_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_rate": 0.0,
|
||||||
|
"traversal_cutoff_rollouts": 0,
|
||||||
|
"traversal_cutoff_rollout_steps": 0,
|
||||||
|
"traversal_cutoff_rollout_timeouts": 0,
|
||||||
|
"traversal_endpoint_depth_sum": 8062,
|
||||||
|
"traversal_endpoints": 32,
|
||||||
|
"traversal_avg_endpoint_depth": 251.9375,
|
||||||
|
"traversal_endpoint_depth_bucket_200_299": 28,
|
||||||
|
"traversal_endpoint_depth_bucket_300_399": 3,
|
||||||
|
"traversal_endpoint_depth_bucket_100_199": 1,
|
||||||
|
"advantage_samples_returned": 4048,
|
||||||
|
"strategy_samples_returned": 4048
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traversal_nodes": 12960,
|
||||||
|
"traversal_terminals": 32,
|
||||||
|
"traversal_depth_cutoffs": 0,
|
||||||
|
"traversal_node_limit_cutoffs": 0,
|
||||||
|
"traversal_max_depth_reached": 598,
|
||||||
|
"traversal_advantage_samples": 6470,
|
||||||
|
"traversal_strategy_samples": 6470,
|
||||||
|
"traversal_sampled_actions": 6470,
|
||||||
|
"traversal_regret_matching_decisions": 6470,
|
||||||
|
"traversal_regret_fallback_count": 971,
|
||||||
|
"traversal_regret_fallback_rate": 0.1500772797527048,
|
||||||
|
"traversal_regret_fallback_avg_depth": 121.39546858908342,
|
||||||
|
"traversal_regret_fallback_action_play_existing": 0,
|
||||||
|
"traversal_regret_fallback_action_open_new": 0,
|
||||||
|
"traversal_regret_fallback_action_discard": 0,
|
||||||
|
"traversal_regret_fallback_action_draw_deck": 372,
|
||||||
|
"traversal_regret_fallback_action_draw_pile": 599,
|
||||||
|
"traversal_regret_fallback_legal_actions_mean": 2.818743563336766,
|
||||||
|
"traversal_regret_fallback_legal_play_existing_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_open_new_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_discard_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_deck_mean": 1.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_pile_mean": 1.8187435633367661,
|
||||||
|
"traversal_regret_fallback_open_new_available_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selected": 0,
|
||||||
|
"traversal_regret_fallback_open_new_selected_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selection_over_availability": 0.0,
|
||||||
|
"traversal_regret_fallback_avg_opened_colors_before_action": 3.9742533470648818,
|
||||||
|
"traversal_regret_fallback_argmax_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_size_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_rate": 0.0,
|
||||||
|
"traversal_cutoff_rollouts": 0,
|
||||||
|
"traversal_cutoff_rollout_steps": 0,
|
||||||
|
"traversal_cutoff_rollout_timeouts": 0,
|
||||||
|
"traversal_endpoint_depth_sum": 12928,
|
||||||
|
"traversal_endpoints": 32,
|
||||||
|
"traversal_avg_endpoint_depth": 404.0,
|
||||||
|
"traversal_endpoint_depth_bucket_300_399": 16,
|
||||||
|
"traversal_endpoint_depth_bucket_500_599": 3,
|
||||||
|
"traversal_endpoint_depth_bucket_400_499": 12,
|
||||||
|
"traversal_endpoint_depth_bucket_200_299": 1,
|
||||||
|
"traversal_regret_fallback_depth_bucket_300_349": 45,
|
||||||
|
"traversal_regret_fallback_depth_bucket_200_249": 98,
|
||||||
|
"traversal_regret_fallback_depth_bucket_150_199": 98,
|
||||||
|
"traversal_regret_fallback_depth_bucket_100_149": 149,
|
||||||
|
"traversal_regret_fallback_depth_bucket_50_99": 194,
|
||||||
|
"traversal_regret_fallback_depth_bucket_0_49": 306,
|
||||||
|
"traversal_regret_fallback_depth_bucket_250_299": 60,
|
||||||
|
"traversal_regret_fallback_depth_bucket_350_399": 16,
|
||||||
|
"traversal_regret_fallback_depth_bucket_400_plus": 5,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_5": 503,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_4": 213,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_2": 83,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_1": 62,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_3": 88,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_0": 22,
|
||||||
|
"advantage_samples_returned": 6470,
|
||||||
|
"strategy_samples_returned": 6470
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traversal_nodes": 13732,
|
||||||
|
"traversal_terminals": 32,
|
||||||
|
"traversal_depth_cutoffs": 0,
|
||||||
|
"traversal_node_limit_cutoffs": 0,
|
||||||
|
"traversal_max_depth_reached": 576,
|
||||||
|
"traversal_advantage_samples": 6852,
|
||||||
|
"traversal_strategy_samples": 6852,
|
||||||
|
"traversal_sampled_actions": 6852,
|
||||||
|
"traversal_regret_matching_decisions": 6852,
|
||||||
|
"traversal_regret_fallback_count": 199,
|
||||||
|
"traversal_regret_fallback_rate": 0.029042615294804435,
|
||||||
|
"traversal_regret_fallback_avg_depth": 49.54271356783919,
|
||||||
|
"traversal_regret_fallback_action_play_existing": 0,
|
||||||
|
"traversal_regret_fallback_action_open_new": 0,
|
||||||
|
"traversal_regret_fallback_action_discard": 0,
|
||||||
|
"traversal_regret_fallback_action_draw_deck": 180,
|
||||||
|
"traversal_regret_fallback_action_draw_pile": 19,
|
||||||
|
"traversal_regret_fallback_legal_actions_mean": 1.5577889447236182,
|
||||||
|
"traversal_regret_fallback_legal_play_existing_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_open_new_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_discard_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_deck_mean": 1.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_pile_mean": 0.5577889447236181,
|
||||||
|
"traversal_regret_fallback_open_new_available_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selected": 0,
|
||||||
|
"traversal_regret_fallback_open_new_selected_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selection_over_availability": 0.0,
|
||||||
|
"traversal_regret_fallback_avg_opened_colors_before_action": 2.4673366834170856,
|
||||||
|
"traversal_regret_fallback_argmax_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_size_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_rate": 0.0,
|
||||||
|
"traversal_cutoff_rollouts": 0,
|
||||||
|
"traversal_cutoff_rollout_steps": 0,
|
||||||
|
"traversal_cutoff_rollout_timeouts": 0,
|
||||||
|
"traversal_endpoint_depth_sum": 13700,
|
||||||
|
"traversal_endpoints": 32,
|
||||||
|
"traversal_avg_endpoint_depth": 428.125,
|
||||||
|
"traversal_endpoint_depth_bucket_400_499": 12,
|
||||||
|
"traversal_endpoint_depth_bucket_300_399": 12,
|
||||||
|
"traversal_endpoint_depth_bucket_200_299": 2,
|
||||||
|
"traversal_endpoint_depth_bucket_500_599": 6,
|
||||||
|
"traversal_regret_fallback_depth_bucket_100_149": 18,
|
||||||
|
"traversal_regret_fallback_depth_bucket_0_49": 125,
|
||||||
|
"traversal_regret_fallback_depth_bucket_50_99": 46,
|
||||||
|
"traversal_regret_fallback_depth_bucket_150_199": 8,
|
||||||
|
"traversal_regret_fallback_depth_bucket_250_299": 1,
|
||||||
|
"traversal_regret_fallback_depth_bucket_200_249": 1,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_4": 38,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_3": 36,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_2": 34,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_1": 43,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_0": 24,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_5": 24,
|
||||||
|
"advantage_samples_returned": 6852,
|
||||||
|
"strategy_samples_returned": 6852
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"encode_legal": {
|
||||||
|
"median_us": 3.096007276326418,
|
||||||
|
"p95_us": 3.8069847505539656,
|
||||||
|
"mean_legal_actions": 6.955078125,
|
||||||
|
"checksum": 14244.0
|
||||||
|
},
|
||||||
|
"push_pop": {
|
||||||
|
"median_us": 0.1510197762399912,
|
||||||
|
"p95_us": 0.22101448848843575,
|
||||||
|
"checksum": 26295
|
||||||
|
},
|
||||||
|
"policy_boundary_bs1": {
|
||||||
|
"median_us": 111.50000500492752,
|
||||||
|
"p95_us": 125.45599020086229,
|
||||||
|
"checksum": 2128.840651668608
|
||||||
|
},
|
||||||
|
"torch_forward": {
|
||||||
|
"1": {
|
||||||
|
"median_us_per_call": 88.96699000615627,
|
||||||
|
"p95_us_per_call": 97.41401299834251,
|
||||||
|
"checksum": 1910.6115314364433
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"median_us_per_call": 42.4988720624242,
|
||||||
|
"p95_us_per_call": 46.12924385583028,
|
||||||
|
"checksum": 1910.611463546753
|
||||||
|
},
|
||||||
|
"8": {
|
||||||
|
"median_us_per_call": 26.491186872590333,
|
||||||
|
"p95_us_per_call": 28.330872737569734,
|
||||||
|
"checksum": 1910.611445426941
|
||||||
|
},
|
||||||
|
"64": {
|
||||||
|
"median_us_per_call": 12.843968761444557,
|
||||||
|
"p95_us_per_call": 13.137109363015043,
|
||||||
|
"checksum": 1910.6114654541016
|
||||||
|
},
|
||||||
|
"256": {
|
||||||
|
"median_us_per_call": 11.324570266424416,
|
||||||
|
"p95_us_per_call": 11.40932033649733,
|
||||||
|
"checksum": 1910.6114501953125
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
{
|
||||||
|
"config": "configs/deep_cfr/default.yaml",
|
||||||
|
"device": "cuda",
|
||||||
|
"torch_threads": 1,
|
||||||
|
"traversals": 8,
|
||||||
|
"runs": 3,
|
||||||
|
"warmup": 1,
|
||||||
|
"corpus_size": 512,
|
||||||
|
"input_dim": 365,
|
||||||
|
"action_size": 22,
|
||||||
|
"full_traversal": {
|
||||||
|
"seconds": [
|
||||||
|
0.3639091220102273,
|
||||||
|
0.623861116997432,
|
||||||
|
0.5022158679785207
|
||||||
|
],
|
||||||
|
"median_seconds": 0.5022158679785207,
|
||||||
|
"median_nodes": 3010.0,
|
||||||
|
"median_policy_calls": 1502.0,
|
||||||
|
"median_us_per_node": 163.2289683405107,
|
||||||
|
"median_us_per_policy_call": 326.97123532360166,
|
||||||
|
"stats": [
|
||||||
|
{
|
||||||
|
"traversal_nodes": 2256,
|
||||||
|
"traversal_terminals": 8,
|
||||||
|
"traversal_depth_cutoffs": 0,
|
||||||
|
"traversal_node_limit_cutoffs": 0,
|
||||||
|
"traversal_max_depth_reached": 398,
|
||||||
|
"traversal_advantage_samples": 1126,
|
||||||
|
"traversal_strategy_samples": 1126,
|
||||||
|
"traversal_sampled_actions": 1126,
|
||||||
|
"traversal_regret_matching_decisions": 1126,
|
||||||
|
"traversal_regret_fallback_count": 35,
|
||||||
|
"traversal_regret_fallback_rate": 0.03108348134991119,
|
||||||
|
"traversal_regret_fallback_avg_depth": 113.8,
|
||||||
|
"traversal_regret_fallback_action_play_existing": 0,
|
||||||
|
"traversal_regret_fallback_action_open_new": 0,
|
||||||
|
"traversal_regret_fallback_action_discard": 0,
|
||||||
|
"traversal_regret_fallback_action_draw_deck": 19,
|
||||||
|
"traversal_regret_fallback_action_draw_pile": 16,
|
||||||
|
"traversal_regret_fallback_legal_actions_mean": 3.057142857142857,
|
||||||
|
"traversal_regret_fallback_legal_play_existing_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_open_new_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_discard_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_deck_mean": 1.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_pile_mean": 2.057142857142857,
|
||||||
|
"traversal_regret_fallback_open_new_available_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selected": 0,
|
||||||
|
"traversal_regret_fallback_open_new_selected_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selection_over_availability": 0.0,
|
||||||
|
"traversal_regret_fallback_avg_opened_colors_before_action": 4.057142857142857,
|
||||||
|
"traversal_regret_fallback_argmax_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_size_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_rate": 0.0,
|
||||||
|
"traversal_cutoff_rollouts": 0,
|
||||||
|
"traversal_cutoff_rollout_steps": 0,
|
||||||
|
"traversal_cutoff_rollout_timeouts": 0,
|
||||||
|
"traversal_endpoint_depth_sum": 2248,
|
||||||
|
"traversal_endpoints": 8,
|
||||||
|
"traversal_avg_endpoint_depth": 281.0,
|
||||||
|
"traversal_endpoint_depth_bucket_300_399": 2,
|
||||||
|
"traversal_endpoint_depth_bucket_200_299": 6,
|
||||||
|
"traversal_regret_fallback_depth_bucket_150_199": 7,
|
||||||
|
"traversal_regret_fallback_depth_bucket_50_99": 5,
|
||||||
|
"traversal_regret_fallback_depth_bucket_0_49": 11,
|
||||||
|
"traversal_regret_fallback_depth_bucket_350_399": 1,
|
||||||
|
"traversal_regret_fallback_depth_bucket_200_249": 4,
|
||||||
|
"traversal_regret_fallback_depth_bucket_100_149": 7,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_5": 24,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_0": 3,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_4": 4,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_1": 3,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_3": 1,
|
||||||
|
"advantage_samples_returned": 1126,
|
||||||
|
"strategy_samples_returned": 1126
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traversal_nodes": 3822,
|
||||||
|
"traversal_terminals": 8,
|
||||||
|
"traversal_depth_cutoffs": 0,
|
||||||
|
"traversal_node_limit_cutoffs": 0,
|
||||||
|
"traversal_max_depth_reached": 560,
|
||||||
|
"traversal_advantage_samples": 1908,
|
||||||
|
"traversal_strategy_samples": 1908,
|
||||||
|
"traversal_sampled_actions": 1908,
|
||||||
|
"traversal_regret_matching_decisions": 1908,
|
||||||
|
"traversal_regret_fallback_count": 96,
|
||||||
|
"traversal_regret_fallback_rate": 0.050314465408805034,
|
||||||
|
"traversal_regret_fallback_avg_depth": 64.83333333333333,
|
||||||
|
"traversal_regret_fallback_action_play_existing": 0,
|
||||||
|
"traversal_regret_fallback_action_open_new": 0,
|
||||||
|
"traversal_regret_fallback_action_discard": 0,
|
||||||
|
"traversal_regret_fallback_action_draw_deck": 47,
|
||||||
|
"traversal_regret_fallback_action_draw_pile": 49,
|
||||||
|
"traversal_regret_fallback_legal_actions_mean": 1.90625,
|
||||||
|
"traversal_regret_fallback_legal_play_existing_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_open_new_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_discard_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_deck_mean": 1.0,
|
||||||
|
"traversal_regret_fallback_legal_draw_pile_mean": 0.90625,
|
||||||
|
"traversal_regret_fallback_open_new_available_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selected": 0,
|
||||||
|
"traversal_regret_fallback_open_new_selected_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selection_over_availability": 0.0,
|
||||||
|
"traversal_regret_fallback_avg_opened_colors_before_action": 3.25,
|
||||||
|
"traversal_regret_fallback_argmax_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_size_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_rate": 0.0,
|
||||||
|
"traversal_cutoff_rollouts": 0,
|
||||||
|
"traversal_cutoff_rollout_steps": 0,
|
||||||
|
"traversal_cutoff_rollout_timeouts": 0,
|
||||||
|
"traversal_endpoint_depth_sum": 3814,
|
||||||
|
"traversal_endpoints": 8,
|
||||||
|
"traversal_avg_endpoint_depth": 476.75,
|
||||||
|
"traversal_endpoint_depth_bucket_400_499": 4,
|
||||||
|
"traversal_endpoint_depth_bucket_300_399": 1,
|
||||||
|
"traversal_endpoint_depth_bucket_500_599": 3,
|
||||||
|
"traversal_regret_fallback_depth_bucket_300_349": 1,
|
||||||
|
"traversal_regret_fallback_depth_bucket_200_249": 4,
|
||||||
|
"traversal_regret_fallback_depth_bucket_150_199": 4,
|
||||||
|
"traversal_regret_fallback_depth_bucket_50_99": 24,
|
||||||
|
"traversal_regret_fallback_depth_bucket_0_49": 50,
|
||||||
|
"traversal_regret_fallback_depth_bucket_100_149": 13,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_5": 24,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_4": 22,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_3": 20,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_1": 14,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_2": 15,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_0": 1,
|
||||||
|
"advantage_samples_returned": 1908,
|
||||||
|
"strategy_samples_returned": 1908
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traversal_nodes": 3010,
|
||||||
|
"traversal_terminals": 8,
|
||||||
|
"traversal_depth_cutoffs": 0,
|
||||||
|
"traversal_node_limit_cutoffs": 0,
|
||||||
|
"traversal_max_depth_reached": 532,
|
||||||
|
"traversal_advantage_samples": 1502,
|
||||||
|
"traversal_strategy_samples": 1502,
|
||||||
|
"traversal_sampled_actions": 1502,
|
||||||
|
"traversal_regret_matching_decisions": 1502,
|
||||||
|
"traversal_regret_fallback_count": 72,
|
||||||
|
"traversal_regret_fallback_rate": 0.047936085219707054,
|
||||||
|
"traversal_regret_fallback_avg_depth": 246.29166666666666,
|
||||||
|
"traversal_regret_fallback_action_play_existing": 1,
|
||||||
|
"traversal_regret_fallback_action_open_new": 0,
|
||||||
|
"traversal_regret_fallback_action_discard": 54,
|
||||||
|
"traversal_regret_fallback_action_draw_deck": 17,
|
||||||
|
"traversal_regret_fallback_action_draw_pile": 0,
|
||||||
|
"traversal_regret_fallback_legal_actions_mean": 6.375,
|
||||||
|
"traversal_regret_fallback_legal_play_existing_mean": 0.027777777777777776,
|
||||||
|
"traversal_regret_fallback_legal_open_new_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_legal_discard_mean": 6.111111111111111,
|
||||||
|
"traversal_regret_fallback_legal_draw_deck_mean": 0.2361111111111111,
|
||||||
|
"traversal_regret_fallback_legal_draw_pile_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_available_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selected": 0,
|
||||||
|
"traversal_regret_fallback_open_new_selected_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_open_new_selection_over_availability": 0.0,
|
||||||
|
"traversal_regret_fallback_avg_opened_colors_before_action": 4.638888888888889,
|
||||||
|
"traversal_regret_fallback_argmax_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_rate": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_tie_size_mean": 0.0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_count": 0,
|
||||||
|
"traversal_regret_fallback_argmax_full_tie_rate": 0.0,
|
||||||
|
"traversal_cutoff_rollouts": 0,
|
||||||
|
"traversal_cutoff_rollout_steps": 0,
|
||||||
|
"traversal_cutoff_rollout_timeouts": 0,
|
||||||
|
"traversal_endpoint_depth_sum": 3002,
|
||||||
|
"traversal_endpoints": 8,
|
||||||
|
"traversal_avg_endpoint_depth": 375.25,
|
||||||
|
"traversal_endpoint_depth_bucket_300_399": 7,
|
||||||
|
"traversal_endpoint_depth_bucket_500_599": 1,
|
||||||
|
"traversal_regret_fallback_depth_bucket_300_349": 11,
|
||||||
|
"traversal_regret_fallback_depth_bucket_250_299": 18,
|
||||||
|
"traversal_regret_fallback_depth_bucket_150_199": 5,
|
||||||
|
"traversal_regret_fallback_depth_bucket_0_49": 17,
|
||||||
|
"traversal_regret_fallback_depth_bucket_200_249": 6,
|
||||||
|
"traversal_regret_fallback_depth_bucket_400_plus": 11,
|
||||||
|
"traversal_regret_fallback_depth_bucket_350_399": 4,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_5": 62,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_2": 2,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_3": 2,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_4": 3,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_1": 2,
|
||||||
|
"traversal_regret_fallback_opened_colors_count_0": 1,
|
||||||
|
"advantage_samples_returned": 1502,
|
||||||
|
"strategy_samples_returned": 1502
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"encode_legal": {
|
||||||
|
"median_us": 3.156019374728203,
|
||||||
|
"p95_us": 3.877998096868396,
|
||||||
|
"mean_legal_actions": 6.955078125,
|
||||||
|
"checksum": 7122.0
|
||||||
|
},
|
||||||
|
"push_pop": {
|
||||||
|
"median_us": 0.16100239008665085,
|
||||||
|
"p95_us": 0.2500019036233425,
|
||||||
|
"checksum": 13048
|
||||||
|
},
|
||||||
|
"policy_boundary_bs1": {
|
||||||
|
"median_us": 181.29700038116425,
|
||||||
|
"p95_us": 194.76699526421726,
|
||||||
|
"checksum": 998.0805744677782
|
||||||
|
},
|
||||||
|
"torch_forward": {
|
||||||
|
"1": {
|
||||||
|
"median_us_per_call": 141.6669983882457,
|
||||||
|
"p95_us_per_call": 156.93597379140556,
|
||||||
|
"checksum": 850.3550980091095
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"median_us_per_call": 34.81799649307504,
|
||||||
|
"p95_us_per_call": 37.939003959763795,
|
||||||
|
"checksum": 850.355094909668
|
||||||
|
},
|
||||||
|
"8": {
|
||||||
|
"median_us_per_call": 19.62381247722078,
|
||||||
|
"p95_us_per_call": 21.306375856511295,
|
||||||
|
"checksum": 850.3550970554352
|
||||||
|
},
|
||||||
|
"64": {
|
||||||
|
"median_us_per_call": 2.5487893253739458,
|
||||||
|
"p95_us_per_call": 2.75081220024731,
|
||||||
|
"checksum": 850.3550720214844
|
||||||
|
},
|
||||||
|
"256": {
|
||||||
|
"median_us_per_call": 0.6167675792312366,
|
||||||
|
"p95_us_per_call": 0.7250391718116589,
|
||||||
|
"checksum": 850.3551635742188
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user