Phase B: cross-platform dimension-sweep benchmark - #80
Conversation
Epic #74 Phase B: measure both results and performance for every AMICA backend across CPU/MPS/CUDA/MLX on real 70-channel EEG (OpenNeuro ds002718 sub-002), sweeping channel count and n_models with component sharing on/off. benchmarks/benchmark_dimsweep.py auto-detects the host's backends (numpy/torch-cpu/torch-mps/torch-cuda/mlx), records ms/iteration (warmed, min-of-repeats) and converged LL at matched settings, and emits JSON so a Mac run and a CUDA host run merge via --report. MLX (single-model MVP) auto-excludes from multi-model/sharing configs. Data is fetched from OpenNeuro, not committed (README_dimsweep.md; benchmarks/data + result JSONs gitignored). Findings (.context/issue-77/benchmark_findings.md): - MLX is the Apple-GPU win: ~15-25 ms/it, flat across 16-70 ch, ~7x over torch-CPU and faster than an RTX 4090 (CUDA ~36 ms) at EEG scale. - PyTorch-MPS never wins (162-255 ms/it, at or worse than CPU) -- use MLX, not device="mps", on Apple hardware. - Results agree across cpu/mps/cuda/mlx and f32/f64 to ~3 digits on real data. - Multi-model has no GPU path yet (MLX MVP is single-model; MPS loses), so multi-model MLX is the top fast-follow. Docs updated: mps_pathways.md Pathway B (DONE), AGENTS.md perf note.
The numpy adapter didn't pass block_size, so AMICA_NumPy used its default (block_size=128, do_opt_block=True) and ran an in-fit wall-clock block-size auto-tune search inside the timed region -- unmatched vs torch/mlx (which pin block_size=512) and timing pollution that ~doubled numpy's ms/it. Pass block_size=512, do_opt_block=False. Corrected numpy: 142-622 ms/it (was 310-1350), so numpy is ~10-25x slower than MLX, not 50-70x. Results (LL) unchanged (block_size does not affect the accumulated sufficient stats). Findings table updated; the qualitative conclusions (MLX ~7x win, MPS never wins) are unaffected.
Review (Sonnet code-reviewer, worktree-isolated)One valid finding (confidence 84), fixed in 8f26b39; everything else confirmed correct. Fixed: the numpy adapter didn't pin Confirmed correct by the reviewer (cross-referenced against the actual backends): the numpy LL normalization ( |
b923b8a
into
feature/issue-74-epic-apple-gpu
Summary
Epic #74 Phase B: the benchmark that answers what Phases A and C were building toward -- does an Apple/NVIDIA GPU actually beat the CPU for AMICA, and where? Measures both results (converged LL) and performance (ms/it) for every backend (numpy / torch-cpu / torch-mps / torch-cuda / mlx) on real 70-channel EEG (OpenNeuro ds002718 sub-002), sweeping channel count and
n_modelswith component sharing on/off.benchmarks/benchmark_dimsweep.pyauto-detects the host's backends, records warmed min-of-repeats timing + LL at matched settings, and emits JSON so a Mac run and a CUDA-host run merge via--report. MLX (single-model MVP) auto-excludes from multi-model/sharing configs. Data is fetched from OpenNeuro (public), not committed (README_dimsweep.md;benchmarks/data/+ result JSONs gitignored).Headline results (single-model, ms/iteration)
device="mps".Full tables + caveats:
.context/issue-77/benchmark_findings.md.Test plan
This is a benchmark harness (no unit tests / not run in CI). Validated by running it: full single-model sweep on Apple Silicon (cpu/mps/mlx) + RTX 4090 (cuda), and multi-model (m2, m2+share) on Apple Silicon.
--reportmerges the per-platform JSONs into ms/it + LL tables. ruff clean.Caveats / pending
--n-models 2 [--share] --backends torch-cuda-*on the CUDA host to fill it in.Closes #77
Part of epic #74