Benchmarks

Every number on this page is measured, never asserted. The tables below are rendered at docs-build time straight from the committed baseline JSON (benchmarks/results/baseline-<host>.json) — the docs cannot show a figure the suite did not produce. Each scenario states its hypothesis and its measurement method in its own source file (benchmarks/scenarios/), so the methodology is auditable from the code alone, and every caveat and skip is printed verbatim in the notes under each table.

Where a comparator (ray, accelerate) is not installed on the baseline machine, its row says so and its setup code is counted, not run, from the cited fixtures in benchmarks/scenarios/snippets/ — an honest line count, never a fabricated timing. Some figures here are small, zero, or negative: that is the suite working as intended. On a tiny CPU model, process-startup dominates wall-clock and a checkpoint write or a 40-step recompute falls below the run-to-run noise floor — the notes say so, and the size-independent guarantees (e.g. steps_not_recomputed) are reported alongside. The value shows up at real model scale; the honesty shows up here.

The suite is split into two sections, each rendered as its own table below. Performance measures the overheads a user pays for adopting flashruntime — launch overhead, per-checkpoint cost, submit latency, fan-out throughput, adoption line count — as wall-clock medians against a bare-torchrun or plain sequential baseline. Resilience measures the fault-tolerance guarantees themselves, under real failure injection rather than modelling:

Each resilience number is COUNTED or timed from observable run state, never baked into an assertion. The failure injection lives in benchmarks/faults.py (crashy-trainer generation, kill -9 timing, part corruption), each scenario's measurement method is stated in its own source file under benchmarks/scenarios/, and the long chaos loops carry a bench_stress marker so pytest -m bench_stress re-runs them.

Reproduce the whole baseline yourself:

python -m benchmarks run --all --repeats 5

Run a single scenario, or a fast labelled smoke:

python -m benchmarks run --scenario recovery_economics
python -m benchmarks run --all --smoke

Measured on:

os cpu cores ram_gb python torch flashruntime
macOS-26.5.1-arm64-arm-64bit Apple M4 10 16.0 3.11.15 2.13.0 0.1.0

Reproduce every number below with:

python -m benchmarks run --all --repeats 5

Summary

Performance

scenario median unit p10 p90 repeats
adoption_cost 7 lines to adopt 7 7 20
fanout_throughput 111 tasks/min 111 111 16
hpo_sweep 6.12 seconds 5.98 6.26 20
launch_overhead 0.0443 seconds -0.203 0.24 20
loop_overhead -1.25 ms/checkpoint -13.7 6.19 20
recovery_economics 0.259 seconds saved -0.368 2.44 20
submit_latency 0.0249 s (p50 cold) 0.0239 0.0259 20

Resilience

scenario median unit p10 p90 repeats
checkpoint_integrity 1 integrity_rate 1 1 20
crash_storm 16 completed/16 16 16 20
fault_recovery_matrix 5 correct/5 5 5 20
lease_recovery_latency 0.0035 s (MTTR) 0.0035 0.0035 20

adoption_cost

Hypothesis: Adopting flashruntime is a handful of lines and a tiny dependency footprint — less code and a faster import than ray or accelerate.

Measured: 7 lines to adopt (p10 7, p90 7, 20 repeats)

comparator value
accelerate_adopt_loc 8
flashruntime_core_deps 1
flashruntime_import_ms 74.9
torch_import_ms 692

fanout_throughput

Hypothesis: A local flash.submit fan-out runs at a tasks/minute rate close to a plain sequential subprocess loop (fan-out is sequential by design), and its per-task overhead amortizes away as task duration grows.

Measured: 111 tasks/min (p10 111, p90 111, 16 repeats)

comparator value
sequential_tasks_min 110
overhead_frac_05s 0.078
overhead_frac_2s 0.022
overhead_frac_5s 0.008

hpo_sweep

Hypothesis: flashruntime runs an 8-trial sweep with far less setup code than a hand-rolled loop or ray.tune, at comparable local wall-clock.

Measured: 6.12 seconds (p10 5.98, p90 6.26, 20 repeats)

comparator value
sequential_s 5.98
peak_child_rss_mb 203
flash_setup_loc 10
sequential_setup_loc 16
ray_tune_setup_loc 22

launch_overhead

Hypothesis: flash.submit adds under a second of wall-clock over a bare torchrun launch.

Measured: 0.0443 seconds (p10 -0.203, p90 0.24, 20 repeats)

comparator value
flash_submit_s 2.53
bare_torchrun_s 2.46

loop_overhead

Hypothesis: The ft training loop adds negligible per-step cost; a checkpoint is a few to low-tens of ms.

Measured: -1.25 ms/checkpoint (p10 -13.7, p90 6.19, 20 repeats)

comparator value
ft_steps_per_s 165
vanilla_steps_per_s 88.9
ft_vs_vanilla_wall_per_step_ratio 0.542

recovery_economics

Hypothesis: Auto-resume from a checkpoint finishes a crashed run faster than a raw rerun-from-zero, and never recomputes the steps past the last checkpoint.

Measured: 0.259 seconds saved (p10 -0.368, p90 2.44, 20 repeats)

comparator value
auto_resume_s 5
raw_rerun_from_zero_s 5.28
steps_not_recomputed 40

submit_latency

Hypothesis: flash.submit adds a small, roughly constant overhead before user code hits its first step — dominated by child-process launch, not flashruntime bookkeeping — invisible next to any real training run.

Measured: 0.0249 s (p50 cold) (p10 0.0239, p90 0.0259, 20 repeats)

comparator value
cold_p95 0.0262
warm_p50 0.0258
warm_p95 0.035
phase_launch_s 0.0239
phase_child_s 0.0009

checkpoint_integrity

Hypothesis: Under repeated kill -9s inside the checkpoint write window, flashruntime's parts-first/manifest-last commit means every resume lands on a hash-verified manifest (integrity_rate → 1.0), while a naive torch.save('latest.pt') overwriting one file in place is truncated by the same kill and fails to reload.

Measured: 1 integrity_rate (p10 1, p90 1, 20 repeats)

comparator value
iterations 20
torn_writes_hit 20
window_missed 0
naive_torch_save_failure_rate 1
naive_torn_writes_hit 20

crash_storm

Hypothesis: A 16-trial fan-out where every even trial crashes mid-run still completes 16/16 with zero human interventions — flashruntime auto-resumes each crash from its own checkpoint — at a bounded, MEASURED goodput and wall-clock cost.

Measured: 16 completed/16 (p10 16, p90 16, 20 repeats)

comparator value
goodput_lower_bound 0.8
recompute_fraction 0
wallclock_penalty_fraction 0.243
manual_interventions 0
crashed_first_attempt 8

fault_recovery_matrix

Hypothesis: Automated recovery does the right typed thing across a fault matrix — fail-fast on a deterministic bug, resume from the newest valid checkpoint on transient crashes/mid-run kills/mid-write kills/corruption — with zero human interventions where a bare torchrun needs one per fault.

Measured: 5 correct/5 (p10 5, p90 5, 20 repeats)

comparator value
manual_interventions_flash 0
manual_interventions_torchrun_modelled 5
mean_recovery_s 0.256
mean_steps_preserved 3.3
torchrun_recovery_modelled_s 1.87

lease_recovery_latency

Hypothesis: Mode A detects a dead worker and requeues its task within lease_seconds + 2 s (the sweeper guarantee), and a healthy claim/heartbeat round-trip is a few ms — so recovery time (MTTR) is dominated by the tunable lease window, not coordinator overhead. Measured against the REAL coordinator over real sockets.

Measured: 0.0035 s (MTTR) (p10 0.0035, p90 0.0035, 20 repeats)

comparator value
claim_rt_p50_ms 1.25
claim_rt_p95_ms 1.68
hb_rt_p50_ms 1.18
mttd_s 3.05
mttd_bound_s 5
roundtrips 20