Measured 2026-09-26 with the scorer’s energy column (mnist.py 1.2.0): every
known method that passes a difficulty in under 60 s per call, three runs each,
each run in its own Modal A100-80GB container, sandbox on. Energy is the GPU
board’s energy per call above idle; time is the ranked time per call. Medians
of three runs.
| Method | Difficulty | ms per call | mJ per call above idle | W above idle in a call | MNIST |
|---|---|---|---|---|---|
| CUDA-graph MLP, 60-256-256-10, 200 steps (PR #96) | 1 | 61.6 | 2,429 | 48 | 95.03% |
| eager MLP, 60-1024-1024-10, 100 steps | 1 | 191.3 | 2,648 | 13 | 94.98% |
example.py, eager MLP, 60-1024-1024-10, 400 steps |
1 | 762.4 | 29,662 | 42 | 96.77% |
| CUDA-graph ensemble, 4 x 60-256-256-10, 800 steps | 2 | 252.1 | 14,436 | 57 | 96.87% |
| eager ensemble, 16 x 60-1024-1024-10, 400 steps | 2 | 927.3 | 200,870 | 217 | 96.78% |
| Method | Run | GPU | ms per call | mJ per call | Energy window | Idle W | Round trip mJ | Reference J/TFLOP |
|---|---|---|---|---|---|---|---|---|
| CUDA-graph MLP (PR #96) | smoke | A100 80GB PCIe | 51.0 | 1,441 | 364 calls, 20.0 s, 49.6 ms each | 78.2 | 17.2 | 7.21 |
| CUDA-graph MLP (PR #96) | 1 | A100 80GB PCIe | 51.2 | 1,393 | 359 calls, 20.0 s, 49.7 ms each | 75.9 | 18.9 | 7.74 |
| CUDA-graph MLP (PR #96) | 2 | A100-SXM4-80GB | 61.6 | 2,637 | 355 calls, 20.0 s, 50.1 ms each | 69.3 | 13.2 | 9.22 |
| CUDA-graph MLP (PR #96) | 3 | A100-SXM4-80GB | 62.0 | 2,429 | 343 calls, 20.0 s, 50.3 ms each | 68.6 | 16.6 | 8.94 |
| eager MLP | 1 | A100 80GB PCIe | 168.2 | 3,258 | 116 calls, 20.2 s, 168.1 ms each | 80.9 | 13.3 | 7.61 |
| eager MLP | 2 | A100-SXM4-80GB | 191.3 | 2,370 | 100 calls, 20.1 s, 194.6 ms each | 69.8 | 14.1 | 8.10 |
| eager MLP | 3 | A100-SXM4-80GB | 196.2 | 2,648 | 97 calls, 20.1 s, 197.1 ms each | 67.7 | 17.4 | 8.78 |
example.py |
1 | A100 80GB PCIe | 793.0 | 29,662 | 29 calls, 20.5 s, 702.8 ms each | 82.2 | 19.0 | 7.42 |
example.py |
2 | A100 80GB PCIe | 762.4 | 28,810 | 25 calls, 20.1 s, 784.9 ms each | 72.4 | 18.6 | 7.56 |
example.py |
3 | A100-SXM4-80GB | 486.5 | 37,930 | 38 calls, 20.1 s, 512.3 ms each | 68.0 | 12.7 | 7.98 |
| CUDA-graph ensemble | 1 | A100 80GB PCIe | 251.8 | 14,333 | 77 calls, 20.2 s, 255.4 ms each | 72.9 | 19.1 | 7.40 |
| CUDA-graph ensemble | 2 | A100 80GB PCIe | 252.1 | 14,524 | 77 calls, 20.0 s, 255.2 ms each | 74.0 | 20.1 | 7.33 |
| CUDA-graph ensemble | 3 | A100 80GB PCIe | 253.4 | 14,436 | 79 calls, 20.2 s, 250.8 ms each | 80.0 | 18.6 | 7.69 |
| eager ensemble | 1 | A100 80GB PCIe | 927.3 | 202,072 | 22 calls, 20.5 s, 928.2 ms each | 78.2 | 21.0 | 7.89 |
| eager ensemble | 2 | A100 80GB PCIe | 943.8 | 198,165 | 22 calls, 20.5 s, 927.9 ms each | 85.6 | 20.4 | 7.43 |
| eager ensemble | 3 | A100 80GB PCIe | 922.0 | 200,870 | 22 calls, 20.4 s, 925.8 ms each | 81.0 | 16.5 | 7.60 |
Every run passed its difficulty, and every energy passed every check. The sixteen runs landed on fifteen boards (one SXM4 board served two). The smoke-test run is not in the medians above.
probe_nvml.py measured the telemetry on one A100-SXM4-80GB
(results/probe-a100-80gb.json):
| Measured | Consequence in the scorer |
|---|---|
| The energy counter moves every 100 ms (40 changes in 4 s, gaps 95-106 ms) | No call is read on its own: a 20 s window of back-to-back calls |
| Thirty isolated 74 ms bursts of 8 FP32 matmuls read 8.1-15.4 J against the 16.5 J the same work costs in a long window; their edges alone read -5.0 to 11.1 J | The same |
| Idle drew 60.4 W with no CUDA context, 67.3 W with an idle context and 68.8 W with that process stopped | Idle is measured with the method’s process frozen and its context open |
| A read of the counter takes 3.1 ms, and polled in a tight loop the counter advanced at about 90 W against 60-67 W read sparsely | The counter is read only at window edges |
| The same FP32 matmul drew 351 W (15.0 J/TFLOP above idle) on random operands; the earlier harness’s constant operands read 8.1-8.8 J/TFLOP on six boards | The telemetry check uses constant operands, whose band is known: 6-11 J/TFLOP |
The fifteen boards here read 7.2-9.2 J/TFLOP at 17.2-18.4 TFLOP/s on that check, PCIe cards 7.2-7.9 and SXM4 boards 8.0-9.2. The round trip without the method cost 13-21 mJ per call, at most 1.4% of any method’s energy. In the energy window the GPU ran the method 87-90% of the time for the 50 ms CUDA-graph MLP and 96-99.7% for the others.
From mnist-a100/, with Modal configured (the three ports are generated from the
cutoff study’s mnist/experiments/release-cutoffs-20260925/mlp_timing/submissions):
python energy/probe_nvml.py --out energy/results/probe-a100-80gb.json
python energy/port.py
python run_modal.py energy/entries/fast_mlp.py:fast_mlp --difficulty 1 --runs 3 --json energy/results/fast_mlp
python run_modal.py energy/entries/mlp_k1_w1024_s100_b512.py:mlp --difficulty 1 --runs 3 --json energy/results/mlp_k1_w1024_s100_b512
python run_modal.py example.py:mlp --difficulty 1 --runs 3 --json energy/results/example
python run_modal.py energy/entries/mlpg_k4_w256_s800_b512.py:mlp --difficulty 2 --runs 3 --json energy/results/mlpg_k4_w256_s800_b512
python run_modal.py energy/entries/mlp_k16_w1024_s400_b512.py:mlp --difficulty 2 --runs 3 --json energy/results/mlp_k16_w1024_s400_b512
python energy/summarize.py # the tables above, recomputed from every run's raw energy windows
entries/fast_mlp.py is byte-identical to PR #96’s submission (sha256
44cb0c03d4ac869e99186c584d234c8743c12565e0a3b49cf3172bd5c8a82325). The ports
change only the signature: custom_kernel(data) becomes mlp(train_x, train_y, test_x).
Cost: $2.35 billed by Modal for the seven apps (the probe $0.09, the smoke test $0.12, the fifteen scored runs $2.13).