Performance Playbook¶
This guide details best practices for scaling regression models in torchregress using modern PyTorch features.
Uncertainty-aware training adds a second constraint to the usual throughput/memory trade-off: the numerics that make a model fast (low-precision matmuls, fused kernels, larger batches) can also change the variance and calibration numbers you report. The rule throughout this page is to optimise the model in reduced precision but validate and score in float32/float64, and to measure the effect of every speed-up on both error and calibration rather than on loss alone. The sections below cover AMP, compilation, data loading, and the CUDA test path in that order.
1. Automatic Mixed Precision (AMP)¶
Using float16 or bfloat16 can significantly reduce memory usage and speed up training on compatible hardware.
torchregress provides a simple wrapper:
from torch.amp import autocast, GradScaler
scaler = GradScaler("cuda")
for x, y in dataloader:
optimizer.zero_grad()
with autocast(device_type="cuda", dtype=torch.float16):
loss = model(x, y)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
autocast automatically disables itself when the requested runtime device is unavailable (for example, device_type="cuda" on a CPU-only machine).
Note: For A100/H100 GPUs, prefer
torch.bfloat16as it doesn't strictly require a GradScaler, though using one is still safe. For CPU autocast, prefertorch.bfloat16.
2. Gradient Accumulation¶
When your batch size is limited by GPU memory, use gradient accumulation to simulate a larger effective batch size. torchregress does not ship a helper for this; the loop below uses plain PyTorch.
# Target effective batch size of 256, actual GPU batch size 64
accum_steps = 256 // 64
optimizer.zero_grad()
for i, (x, y) in enumerate(dataloader):
loss = loss_fn(model(x), y)
# Scale so that the accumulated gradient matches the large-batch mean
(loss / accum_steps).backward()
# Only step the optimizer every `accum_steps` mini-batches
if (i + 1) % accum_steps == 0:
optimizer.step()
optimizer.zero_grad()
3. Torch Compile (PyTorch 2.0+)¶
torch.compile can provide significant speedups by fusing operations. torchregress does not wrap it; call it directly on your model.
Supported Modes:¶
default: Balances compile time and runtime performance.reduce-overhead: Optimizes for small batches (high memory overhead).max-autotune: Maximum performance (very long compile time).
4. Generic Tips¶
- Dataloading: Always use
num_workers > 0andpin_memory=Truein your DataLoaders. - Eval Mode: Remember
model.eval()andtorch.no_grad()during inference to save memory. - TF32: On Ampere+ GPUs, enable TF32 for a free speedup:
5. Benchmark Regression Checks (CI)¶
torchregress includes a lightweight benchmark harness for performance smoke checks and
small scale sweeps:
tools/benchmark_smoke.py
Stable CI Invocation (CPU)¶
Use warmup and at least 2 iterations to reduce cold-start noise:
python -m tools.benchmark_smoke \
--mode smoke \
--iterations 2 \
--warmup 1 \
--device cpu \
--thresholds reports/benchmark_thresholds/cpu/smoke.json \
--fail-on-thresholds
python -m tools.benchmark_smoke \
--mode sweep \
--iterations 2 \
--warmup 1 \
--device cpu \
--thresholds reports/benchmark_thresholds/cpu/sweep.json \
--fail-on-thresholds
Threshold Baseline Layout (Per Device)¶
- CPU smoke thresholds:
reports/benchmark_thresholds/cpu/smoke.json - CPU sweep thresholds:
reports/benchmark_thresholds/cpu/sweep.json - CUDA sweep thresholds (reserved path for future CI/hardware baselines):
reports/benchmark_thresholds/cuda/sweep.json
Benchmark Report Summaries¶
Convert JSON benchmark reports into Markdown tables for audit/docs updates:
python -m tools.benchmark_report_summary reports/benchmark_smoke_latest.json
python -m tools.benchmark_report_summary reports/benchmark_sweep_latest.json --group-by-name
CI also renders and uploads Markdown summaries as artifacts (benchmark-cpu-summaries):
reports/benchmark_smoke_latest.mdreports/benchmark_sweep_latest.md
Committed baseline thresholds (CI gate):
reports/benchmark_thresholds/cpu/smoke.jsonreports/benchmark_thresholds/cpu/sweep.json
See Refreshing Threshold Baselines below.
Refreshing Threshold Baselines¶
Re-generate a sweep report and derive thresholds (example for CPU):
python -m tools.benchmark_smoke \
--mode sweep \
--iterations 2 \
--warmup 0 \
--device cpu \
--output reports/benchmark_sweep_cpu_YYYY-MM-DD.json \
--write-thresholds reports/benchmark_thresholds/cpu/sweep.json \
--threshold-multiplier 6.0 \
--threshold-floor-ms 3.0
Re-generate a smoke report and derive smoke thresholds:
python -m tools.benchmark_smoke \
--mode smoke \
--iterations 2 \
--warmup 1 \
--device cpu \
--output reports/benchmark_smoke_YYYY-MM-DD.json \
--write-thresholds reports/benchmark_thresholds/cpu/smoke.json \
--threshold-multiplier 6.0 \
--threshold-floor-ms 3.0
CUDA Benchmark Plan¶
torchregress currently enforces CPU benchmark thresholds in CI. For future GPU CI:
- keep separate baselines per GPU class / runner image
- derive thresholds with higher iteration counts than CPU smoke checks
- version and document the runner hardware in the benchmark artifact
- only enable hard failures after baseline stability is confirmed
The reserved path for a future CUDA sweep baseline is reports/benchmark_thresholds/cuda/sweep.json.
Notes:
- The threshold multiplier/floor are intentionally conservative for CI stability on hosted runners (small sub-millisecond kernels can show occasional jitter spikes).
- For local profiling or true performance comparisons, use a dedicated benchmark workflow with more iterations, repeated runs, and controlled hardware.