Skip to content

Benchmarks

What it costs to build a model and hand it to a solver, in specsolve and in four other ways of writing the same model. No number includes solve time. Every rival model is hand-written in bench/models/, and CI solves each against ours, so nothing here is fast because it built a different model.

dispatch
Meets a demand from a fleet of units: bounds and one balance.
transport
A network of buses and lines: mapping tables, three joins per row.
storage
Dispatch plus a state of charge that carries from each snapshot to the next: a cyclic recurrence over time.
fleet
Twelve declarations rather than one large one: cost per declaration, not per row.
Show Grown by Axes
Libraries
click to hide

Reading the charts

Each panel is one model through one solver, and each line is one library.

  • specsolve is the thick violet line.
  • The grey line is the matrix floor: a hand-written matrix handed straight to the solver's bulk API. Its distance from specsolve is what the modelling layer costs.
  • The line is the median of nine rounds, and the band is the middle half. Overlapping bands are numbers this run cannot tell apart (why the median).
  • A dashed line is a projection past the time or memory budget.
  • The table divides by specsolve. Green is where specsolve is faster, blue where the other library is, grey within ten per cent. >30 s and >16 GB are sizes the harness refused.

A script that solves one model

The charts leave out what a script pays to start: the interpreter, the import and the first build. This table times a new process from launch to exit, on HiGHS.

launch to exitdispatch
10k variables
fleet
12k variables
storage
10k variables
transport
9.8k variables
specsolve0.49 s0.57 s0.51 s0.51 s
specsolve-in-memory0.49 s · 1.0x0.54 s · 0.9x0.49 s · 1.0x0.50 s · 1.0x
linopy1.04 s · 2.1x1.20 s · 2.1x1.06 s · 2.1x1.04 s · 2.0x
pyomo0.72 s · 1.5x1.05 s · 1.9x0.88 s · 1.7x0.84 s · 1.7x
highspy-matrix0.34 s · 0.7x0.35 s · 0.6x0.34 s · 0.7x0.34 s · 0.7x

A script that solves one small model with specsolve finishes in about half a second, under half of linopy's time (#1952).

Not measured yet

  • Sparsity, which is the engine's whole premise. Every published model is dense, the shape an array engine is built for. pixi run density sweeps a sparse one, and no published run has taken it.
  • Solve time. Every number stops when the model reaches the solver.

Method

Each measurement runs in its own process, with import excluded and teardown included, on an idle machine. Peak memory is ru_maxrss. uv run --locked bench/reproduce.py runs it again with every version pinned, and the harness has every flag and caveat, such as the allocator cost on polars' peak (#896).