Benchmarks
Every number on this page comes from a script in the experiments/ directory of the ARC repository, so you can re-run it. Results measured on ARC 5.2.0 come first. Older results come from ARC 5.1 code and are marked as such until they are re-run.
Most recovery results use programmatically injected failures, the standard method in the fault-tolerance literature (ReCycle, SWIFT, Oobleck, Bamboo, JIT-Checkpointing). An organic-failure harness also tests failures caused by real training conditions. Long organic runs at scale are still future work.
Fault-Injection Recovery (ARC 5.2.0)
Protocol: 5 seeds, CPU. Each trial trains the same MLP on the same data twice, once unprotected and once with arc.Controller, and injects the same fault at step 60 of 150. Both runs are judged by the same checks at the end: no exception, every weight finite, and held-out loss no more than 2× its pre-fault value.
Script: experiments/recovery_benchmark.py
| Fault class | Trials | Healthy without ARC | Healthy with ARC | ARC rollbacks |
|---|---|---|---|---|
| No fault (control) | 5 | 5 | 5 | 0 |
| Transient: 14 types (NaN/Inf loss, ×1e10 spikes, NaN weights or optimizer state, LR set to 1e6, Inf and adversarial gradients, repeated bursts) | 70 | 20 | 70 | 173 |
| Persistent bad data (NaN in every batch; ×1e15 loss every 10th step) | 10 | 5 | 10 | 305 |
Under persistent NaN data, rollback can't fix the input. In all 5 of those trials ARC kept the model intact and flagged the run unrecoverable instead of looping. In 20 transient trials ARC rolled back for a fault that unprotected Adam would have absorbed, for example a ×1e10 loss spike. That cost a few steps but never left a worse model. The control group is small, and false-positive rates on full-size training recipes have not been measured yet.
GPU Overhead (ARC 5.2.0)
Protocol: GPT-2 (124M, random init), batch 4, sequence 128, AdamW. 400 steps in interleaved blocks of 50, with and without ARC, after 20 warm-up steps each. The figure is the median step time.
Script: python experiments/controller_overhead.py --steps 400 --batch 4 --seq 128
| GPU | Baseline (ms/step) | With ARC (ms/step) | Overhead |
|---|---|---|---|
| RTX 3050 6GB Laptop | 255.86 | 261.49 | 2.2% |
Snapshots are copied to host memory on a background thread right after optimizer.step(), so the copy overlaps the next forward and backward pass. Gradient and weight checks use one host sync per step. ARC 5.1 measured 40.7% (GPT-2) and 33.4% (GPT-2 Medium) on a Tesla T4 with a different engine and settings, so those numbers aren't directly comparable. A100, H100, and multi-billion-parameter runs have not been profiled yet.
Results from ARC 5.1 (pending re-measurement)
An October 2026 audit found that the 5.1 stress suites (Nightmare, Comprehensive, Silent Failure) never applied their crash checks to the ARC-protected run, so their "100% recovered" figures meant "didn't raise an exception". Those suites are superseded by the symmetric benchmark above and are no longer reported. The results below come from 5.1 code, some from legacy APIs, and are kept for reference until they are re-run on 5.2.
Baseline Comparison
Protocol: 4 methods × 5 failure types × 5 seeds = 25 scenarios per method.
Script: experiments/baseline_comparison.py
| Method | Detection | Recovery | False Pos. | Avg. Time |
|---|---|---|---|---|
| No Protection | 52.0% | 0.0% | 0 | 926ms |
| Gradient Clipping | 20.0% | 0.0% | 0 | 1297ms |
| Loss-Only Monitor | 80.0% | 80.0% | 0 | 1359ms |
| Full ARC | 100% | 100% | 0 | 1722ms |
Detection Rate
Billion-Scale Rollback (H200)
Protocol: NVIDIA H200, PyTorch 2.12. A 40 to 50 step fine-tune with one NaN loss injected at step 30, using the legacy WeightRollback. Each model is run healthy, unprotected with the failure, and ARC-protected with the failure.
Script: experiments/billion_scale_benchmark.py
| Model | Params | No Protection | ARC | Rollbacks | Final Loss (Healthy / ARC) | Peak VRAM |
|---|---|---|---|---|---|---|
| GPT-2 Medium | 355M | Crashed at step 30 | Recovered | 1 | 0.0648 / 0.0936 | 7.5 GB |
| GPT-2 Large | 774M | Crashed at step 30 | Recovered | 1 | 0.0003 / 0.0006 | 16.2 GB |
| GPT-2 XL | 1.56B | Crashed at step 30 | Recovered | 1 | 0.0003 / 0.0006 | 32.0 GB |
| Custom Transformer | 2.65B | Crashed at step 30 | Recovered | 1 | 0.0026 / 0.0053 | 53.5 GB |
The unprotected run is stopped at the NaN by the script. This shows that rollback fits and runs at 2.65B parameters on one GPU. It is not a recovery rate.
Real-Dataset Recovery
These experiments check whether a recovered model reaches the same quality as one trained without interruption. All ran on an NVIDIA Tesla T4.
CIFAR-10, ResNet-18 (3 seeds)
10 epochs of SGD. A NaN loss was injected at epoch 5, step 100.
| Condition | Final Accuracy | Rollbacks | Progress Lost |
|---|---|---|---|
| Clean baseline (no failure) | 77.43% ± 0.12 | 0 | 0 steps |
| ARC-protected (NaN at epoch 5) | 77.42% ± 0.15 | 1.0 | 2.2 steps |
GPT-2 (124M) Fine-Tuning on WikiText-2
300 steps with AdamW. A NaN loss was injected at step 150.
| Condition | Final Perplexity | Rollbacks | Progress Lost |
|---|---|---|---|
| Clean baseline (no failure) | 15.8 | 0 | 0 |
| ARC-protected (NaN at step 150) | 16.4 | 1 | ~5 steps |
Gradient Explosion, ResNet-18 on CIFAR-10 (lr = 10, 5 seeds)
| Method | Survival Rate | Steps Completed (of 100) | Rollbacks |
|---|---|---|---|
| No Protection | 0% (all crashed) | 1.0 | n/a |
| Full ARC | 100% | 99.0 | 1.0 |
Realistic Training Bugs
Protocol: ResNet-50 on CIFAR-100 for 10 epochs on a CUDA GPU. Each run injects a common real-world bug instead of a synthetic NaN. The healthy run reaches 32.66% top-1 accuracy.
Script: experiments/industrial_benchmark.py
| Bug | No Protection | ARC Final Accuracy | Rollbacks |
|---|---|---|---|
| Corrupted batch | Crashed after 4 epochs | 27.66% | 1 |
| Learning-rate bug | Crashed after 4 epochs | 28.39% | 1 |
| Gradient-accumulation bug | Collapsed to 1.04% | 26.20% | 1 |
| AMP overflow | 24.85% | 29.36% | 0 |
In the same benchmark, unprotected GPT-2 and BERT fine-tuning runs crashed mid-training. The ARC-protected runs completed with one rollback each.
Organic Failures
Protocol: 8 failures caused by real training conditions. None of them assigns NaN to the loss directly. Each runs for 60 steps with and without ARC. Data and loss faults start at step 20 and persist. Measured on ARC 5.1.2, CPU.
Script: experiments/organic_failure_test.py
| Scenario | Cause | No ARC | With ARC | Rollbacks |
|---|---|---|---|---|
| LR too high | SGD at lr=3.0 diverges | Loss goes non-finite | Recovered, training continues | 5 |
| NaN row in data | Corrupt sensor row in every batch | Loss goes non-finite | Flagged unrecoverable at step 42 | 24 |
| Zero-variance batch | Std-normalization divides by zero | Loss goes non-finite | Flagged unrecoverable at step 42 | 24 |
log(0) in loss | Saturated softmax, no epsilon guard | Loss goes non-finite | Weights kept finite, flagged unrecoverable at step 42 | 24 |
| Outlier target | One sample with a 1012 target | Loss goes non-finite | Weights kept finite, flagged unrecoverable at step 42 | 24 |
| fp16 overflow | Half precision, inputs scaled 60× | Loss goes non-finite | Weights kept finite, flagged unrecoverable at step 33 | 29 |
| Dead ReLUs (silent) | Large negative bias, gradients vanish | Stays finite, unnoticed | Detected | 1 |
| RNN gradient explosion | Spectral radius > 1, no clipping | Stays finite, unnoticed | Detected | 1 |
ARC detected all 8 failures. It fully recovered the one fault that lived in the optimization itself, a learning rate that was too high. The other five that break an unprotected run sit in the data or the loss expression, so the same bad input comes back after every rollback. ARC kept the weights finite and, after 3 escape cycles, flagged each run unrecoverable within 33 to 42 steps, warning once and naming the input pipeline or loss as the likely cause. Before 5.1.2, these runs looped forever while reporting "recovered".
Large-Model Stress Tests
Protocol: 20 stable steps, then one catastrophic failure, with recovery checked within 10 steps.
Script: experiments/validate_claims_phase2.py
| Model | Params | Failure Type | Recovery | Rollbacks |
|---|---|---|---|---|
| NanoGPT | 10M | LR Spike (50x) | Yes | 2 |
| ResNet-50 | 25.6M | Loss Singularity | Yes | 1 |
| YOLOv11 | 30M | Catastrophic LR | Yes | 3 |
| GPT-2 Small | 50M | NaN Bomb | Yes | 4 |
| SD-UNet | 60M | Gradient Apocalypse | Yes | 4 |
| Wide ResNet | 68M | Loss Supernova | Yes | 3 |
| Llama-style | 70M | Catastrophic LR | Yes | 5 |
| ViT-Base | 86M | Inf Nuke | Yes | 1 |
| GPT-2 Medium | 117M | NaN Bomb | Yes | 3 |
See Billion-Scale Rollback for models from 355M to 2.65B parameters.
Failure Prediction (research)
Protocol: 4 architectures × 5 failure types × 5 seeds × 2 labels = 200 simulated training curves, 5-fold cross-validation.
Script: experiments/prediction_200_v2.py
These classifiers are trained and tested on simulated curves labeled by ARC's rule-based labeler. The scores measure the classifier on synthetic data, not ARC on real runs. No trained predictor ships with the package, so FailurePredictor reports risk_level="unknown" unless you load your own model.
| Classifier | Accuracy | Precision | Recall | F1 |
|---|---|---|---|---|
| Logistic Regression (12f) | 95.5% | 100% | 91.0% | 0.953 |
| MLP (12 features) | 97.5% ± 2.2 | 100% | 95% | 0.974 |
v1 to v2 Improvement
| Metric | v1 (8 feat, LogReg) | v2 (12 feat, MLP) |
|---|---|---|
| Accuracy | 86.5% | 97.5% |
| Recall | 73.0% | 95.0% |
| F1 | 0.844 | 0.974 |
Lyapunov Early Warning
Protocol: The learning rate ramps linearly from stable to explosive, and we record the step at which each detector first fires. Averaged over 5 seeds. Measured on ARC 5.1, before velocity was normalized by learning rate.
| Scenario | LR Range | Lyapunov (step) | Reactive (step) | Lead Time |
|---|---|---|---|---|
| Moderate ramp | 0.01 to 2.0 | 23 | 80 | 57 steps (72%) |
| Steep ramp | 0.01 to 5.0 | 23 | 44 | 26 steps (47%) |
| Gradual ramp | 0.01 to 1.0 | 19 | 120 | 101 steps (84%) |
The lead is largest under gradual destabilization, which is the hardest case for threshold-based detectors. These lead times have not been re-measured on 5.2.
Fisher-Aware vs. Naive Rollback
Protocol: CNN on CIFAR-10. The top third of parameters by Fisher importance are corrupted, then recovered with either a uniform rollback or a Fisher-weighted rollback. Test accuracy is measured after 40 recovery steps and averaged over 5 seeds.
| Corruption Scale | Naive Accuracy | Fisher Accuracy | Delta |
|---|---|---|---|
| 0.02 | 48.7% | 50.2% | +1.5 pp |
| 0.05 | 48.7% | 50.1% | +1.3 pp |
| 0.10 | 48.7% | 49.5% | +0.8 pp |
Ablation Study
Protocol: ResNet-18, 5 seeds, ARC 5.1. The metric is the total number of rollbacks needed to survive each scenario, so lower means more efficient recovery. The three scenarios are (A) a gradient explosion every 10 steps, (B) 60% of weights zeroed at step 25, and (C) a NaN every 5 steps for the whole run. Every configuration survives every scenario.
| Configuration | (A) Instability | (B) Silent Decay | (C) Persistent NaN | Total |
|---|---|---|---|---|
| Full ARC | 1 | 0 | 100 | 101 |
| Without gradient clipping | 40 | 0 | 100 | 140 |
| Without LR reduction | 11 | 0 | 100 | 111 |
| Without weight health | 1 | 0 | 100 | 101 |
| Without weight perturbation | 1 | 0 | 100 | 101 |
| Without forecasting | 1 | 0 | 100 | 101 |
For recurring gradient instability, clipping matters most: without it ARC needed 40× more rollbacks in scenario A. LR reduction comes next (11×) because it breaks the cycle of hitting the same instability again. In 5.1 clipping was on by default. In 5.2 it is opt-in, so set enable_baseline_clipping=True if your run has this pattern. Scenario B did not trip weight-health monitoring under any configuration, because that monitor targets magnitude collapse and explosion, not arbitrary sparsity patterns.
Overhead Breakdown (ARC 5.1, CPU)
Protocol: Median of 100 iterations, time.perf_counter, on a 288K-parameter CNN.
Script: experiments/overhead_measurement.py
| Component | Time (ms) | % of Total |
|---|---|---|
| Gradient Norm | 0.12 | 9.0% |
| Weight Statistics | 1.06 | 76.9% |
| Loss Analysis | 0.01 | 0.6% |
| Checkpoint (amortized) | 0.13 | 9.6% |
| Forecasting | 0.06 | 4.1% |
| Total ARC | 1.38 | 100% |
Added Time by Model Scale (CPU)
ARC's added time per step next to the baseline step time.
| Model | Parameters | ARC (ms) | Baseline (ms) | Relative |
|---|---|---|---|---|
| Small MLP | 50K | 0.86 | 1.45 | ~60% |
| Medium CNN | 288K | 1.38 | 14.17 | ~10% |
| Large CNN | 2.5M | 7.04 | 74.24 | ~9.5% |
Relative overhead falls as models grow, because monitoring cost grows more slowly than the forward and backward passes. 5.2 fuses the weight checks into one host sync, so the weight-statistics share above is an upper bound.
Known Limitations
| Area | Status |
|---|---|
| GPU overhead | 2.2% on GPT-2 (124M) on an RTX 3050. Not yet profiled on A100/H100/H200 or at multi-billion scale |
| Scale | Rollback exercised at 2.65B parameters on one H200 (5.1, legacy engine). Snapshots live in host RAM: plan for max_checkpoints (3) copies of model plus optimizer state |
| Organic failures | Partially tested (8-scenario harness, realistic bugs on ResNet-50). Long organic runs at scale are untested |
| Data faults | Detected but not repairable by rollback. ARC flags them unrecoverable and names the likely cause |
| Distributed training | Controller supports DDP: every rank agrees on each rollback (tested with 2 processes). FSDP/DTensor models are refused with a clear error. Multi-node, straggler, and SDC paths are not yet validated on a live cluster |
| Framework callbacks | The Hugging Face and Lightning hooks don't receive the loss, so they check gradients, weights, and dynamics. With the trainers' default clipping, a finite exploding gradient is clipped before ARC sees it (NaN/Inf is still caught) |
| False positives | 0 rollbacks in 5 clean control runs. Not yet measured on full-size training recipes |
| Multiplicative weight corruption | Scaling all weights (e.g. 1000×) without producing NaN or sparsity is not caught |
| Frameworks | PyTorch only |