# Performance benchmark — v0.9.35 (2026-05-03)

Wall-clock timing of the three classifiers on the 44 canonical
fixtures (one per WRB RSG plus auxiliaries). Mean over 10 iterations
on a single CPU. Cache pre-warmed (one untimed call per system
before measurement).

## Per-pedon mean

| System          | ms / pedon | pedons / sec |
|-----------------|-----------:|-------------:|
| classify_wrb2022 |  22 ms    |  45 pedons/s |
| classify_sibcs   |  32 ms    |  32 pedons/s |
| classify_usda    | 270 ms    |   4 pedons/s |

## Interpretation

WRB and SiBCS are roughly 7-10x faster than USDA per pedon. The
USDA cost is dominated by Subgroup-level testing — `classify_usda()`
walks the full Path C (Order → Suborder → Great Group → Subgroup),
and the Subgroup tier alone is ~85 % of the total runtime. A
benchmark-only run that needs Order / Suborder / Great Group
without Subgroup completes in ~50 ms / pedon.

## Throughput estimates for batch jobs

For typical batch sizes:

| Task                         | WRB only | SiBCS only | USDA only | All three |
|:-----------------------------|---------:|-----------:|----------:|----------:|
| 100 pedons                   | 2 s      | 3 s        | 27 s      | ~30 s     |
| 1 000 pedons                 | 22 s     | 32 s       | 4.5 min   | ~5 min    |
| 10 000 pedons                | 4 min    | 5 min      | 45 min    | ~55 min   |
| KSSL+NASIS n=2638 (validation) | 1 min  | 1.5 min    | 12 min    | ~14 min   |

`benchmark_run_classification()` adds bootstrap-CI cost on top of
the classification time (~20-30 % overhead at default `boot_n =
1000`).

## Comparison with the v0.9.27 KSSL+NASIS validation timing

The v0.9.27 KSSL+NASIS n=3000 4-level benchmark ran in 75 minutes
wall clock. Multi-level benchmarks reuse the classifier output, so
the total cost equals one full classify_usda + bootstrap on each
level, not 4x. The 75-minute observation is consistent with this
profile (~85 % of which was Subgroup-tier work).

## Reproducibility

```r
library(soilKey)
fix_funs <- ls("package:soilKey")
fix_funs <- fix_funs[grepl("^make_.*_canonical$", fix_funs)]
pedons <- lapply(fix_funs, function(fn) get(fn)())

# Warm up cache
invisible(classify_wrb2022(pedons[[1]], on_missing = "silent"))

# Time WRB
t0 <- Sys.time()
for (p in pedons) classify_wrb2022(p, on_missing = "silent")
ms_per_pedon <- 1000 *
  as.numeric(difftime(Sys.time(), t0, units = "secs")) / length(pedons)
ms_per_pedon
```

## Hardware / context

- R version: see `inst/benchmarks/reports/perf_v0935.rds$R_version`
- Single CPU, no parallelism (the classifiers are pure-R; future
  releases could parallelise via `future::future_lapply()`)
- Cache: warm; first-call overhead (rule loading, YAML parse) is
  not counted

The numbers above are fair for users with similar hardware. On a
multi-core machine, parallel batch processing would scale linearly
with cores until I/O bottlenecks kick in.
