Benchmarks
Last synced benchmark assets: 2026-08-24T13:24:43.292589+00:00
System: unknown
CPU: arm, 8 cores
Memory: unknown GB
Platform: macOS-26.3.1-arm64-arm-64bit
Python: 3.12.10, NumPy: 2.3.5, SciPy: 1.16.3, Optyx: 1.3.2
1 Benchmarks
Optyx includes a comprehensive benchmark suite measuring end-to-end performance including variable creation, problem setup, constraint construction, and solving. All benchmarks compare against raw SciPy (which has no build phase).
This page renders directly from synced artifacts in docs/assets/benchmarks/:
benchmark_results.jsonfor structured tables and summary valuesbenchmark_metadata.jsonfor machine and dependency metadatabenchmark_output.txtfor the raw console transcript.pngplots copied from the latest benchmark run
1.1 Quick Start
# Run all benchmark tests
uv run pytest benchmarks/ -v
# Generate performance analysis plots and sync docs assets automatically
uv run python benchmarks/run_benchmarks.pyAll benchmarks measure total time including:
- Variable creation
- Problem setup
- Constraint construction
- Cold solve (first solve, includes compilation)
- Warm solve (cached subsequent solves)
1.2 Performance Summary
| Problem Type | Size | Cold Overhead | Warm Overhead | Notes |
|---|---|---|---|---|
| LP | n=50 | 1.6x | 1.2x | Near-parity with SciPy linprog |
| LP | n=500 | 1.2x | 1.3x | Near-parity with SciPy linprog |
| LP | n=5000 | 1.0x | 1.0x | Scales to large LPs while staying near parity |
| NLP | n=50 | 2.6x | 1.5x | Autodiff overhead on a trivially simple objective |
| NLP | n=500 | 2.3x | 1.3x | Autodiff overhead on a trivially simple objective |
| NLP | n=5000 | 1.4x | 1.4x | Simple quadratic; SciPy converges almost instantly |
| CQP | n=50 | 4.3x | 2.8x | O(1) Jacobian compilation for vectorized constraints |
| CQP | n=500 | 1.6x | 1.2x | O(1) Jacobian compilation for vectorized constraints |
| CQP | n=5000 | 1.1x | 1.0x | Exact Jacobians keep constrained solves near parity |
| MILP | n=50 | 1.3x | 1.2x | Near-parity with SciPy milp |
| MILP | n=500 | 1.2x | 1.1x | Near-parity with SciPy milp |
| MILP | n=5000 | 1.1x | 1.0x | Scales to large binary knapsack problems |
Key Insight: Cold solves include one-time compilation. At n=5,000, the latest warm measurements are 1.0x for LP, CQP, and MILP and 1.2x for the simple NLP case. Small timings are noisier and should not be treated as stable speedup claims.
1.3 LP Scaling: VectorVariable vs Loop-Based

1.3.1 Loop-Based Variables (n ≤ 500)
| n | Build | Cold Solve | Warm Solve | SciPy | Cold Overhead | Warm Overhead |
|---|---|---|---|---|---|---|
| 10 | 0.7ms | 11.8ms | 0.9ms | 0.7ms | 17.0x | 1.3x |
| 25 | 1.2ms | 6.6ms | 1.0ms | 1.0ms | 8.1x | 1.0x |
| 50 | 1.3ms | 24.6ms | 1.3ms | 1.3ms | 20.5x | 1.0x |
| 100 | 5.1ms | 84.2ms | 2.2ms | 2.3ms | 39.6x | 1.0x |
| 200 | 38.9ms | 425.3ms | 5.8ms | 5.5ms | 84.3x | 1.1x |
| 500 | 264.8ms | 2,551.5ms | 36.9ms | 36.5ms | 77.1x | 1.0x |
Loop-based variable construction can create O(n²) expression tree nodes and rapidly increasing compilation time. Use VectorVariable for grouped models.
1.3.2 VectorVariable (n ≤ 5,000)
| n | Build | Cold Solve | Warm Solve | SciPy | Cold Overhead | Warm Overhead |
|---|---|---|---|---|---|---|
| 10 | 0.1ms | 1.4ms | 1.0ms | 1.0ms | 1.6x | 1.0x |
| 25 | 0.1ms | 1.4ms | 1.3ms | 1.0ms | 1.4x | 1.2x |
| 50 | 0.1ms | 2.1ms | 1.6ms | 1.4ms | 1.6x | 1.2x |
| 100 | 0.2ms | 3.7ms | 3.1ms | 2.4ms | 1.6x | 1.3x |
| 200 | 0.3ms | 8.8ms | 7.2ms | 5.7ms | 1.6x | 1.3x |
| 500 | 0.6ms | 43.5ms | 44.9ms | 35.6ms | 1.2x | 1.3x |
| 1000 | 1.2ms | 167.8ms | 154.4ms | 149.4ms | 1.1x | 1.0x |
| 2000 | 2.5ms | 737.5ms | 682.9ms | 665.3ms | 1.1x | 1.0x |
| 5000 | 6.2ms | 6,874.7ms | 6,506.9ms | 6,646.4ms | 1.0x | 1.0x |
In this run, vectorized LP warm overhead ranges from 1.0x to 1.3x and reaches 1.0x at n=5,000. Cold overhead falls from 1.8x at n=50 to 1.1x at n=5,000.
1.4 NLP Scaling: Unconstrained Optimization

Objective: min Σx²ᵢ - Σxᵢ (optimal at x* = 0.5)
1.4.1 VectorVariable with x.dot(x) - x.sum()
| n | Build | Cold Solve | Warm Solve | SciPy | Cold Overhead | Warm Overhead |
|---|---|---|---|---|---|---|
| 10 | 0.0ms | 0.3ms | 0.1ms | 0.1ms | 5.5x | 1.7x |
| 25 | 0.0ms | 0.2ms | 0.1ms | 0.1ms | 3.0x | 1.5x |
| 50 | 0.0ms | 0.2ms | 0.1ms | 0.1ms | 2.6x | 1.5x |
| 100 | 0.0ms | 0.2ms | 0.1ms | 0.1ms | 2.8x | 1.5x |
| 200 | 0.0ms | 0.2ms | 0.1ms | 0.1ms | 2.3x | 1.4x |
| 500 | 0.0ms | 0.2ms | 0.1ms | 0.1ms | 2.3x | 1.3x |
| 1000 | 0.0ms | 1.5ms | 0.3ms | 0.1ms | 11.6x | 2.0x |
| 2000 | 0.0ms | 0.5ms | 0.2ms | 0.2ms | 2.7x | 1.3x |
| 5000 | 0.0ms | 0.7ms | 0.7ms | 0.5ms | 1.4x | 1.4x |
This benchmark uses a trivially simple quadratic (Σx² - Σx) where SciPy’s L-BFGS-B baseline takes roughly 0.06–0.30ms in this run. At that scale, fixed modeling and dispatch costs dominate the ratio. More complex objectives can benefit from exact generated derivatives, but these results do not quantify that benefit.
1.5 Constrained QP Scaling

Objective: min Σx²ᵢ subject to Σxᵢ ≥ 1, xᵢ ≥ 0
1.5.1 VectorVariable with x.dot(x), x.sum()
| n | Build | Cold Solve | Warm Solve | SciPy | Cold Overhead | Warm Overhead |
|---|---|---|---|---|---|---|
| 10 | 0.1ms | 0.7ms | 0.3ms | 0.1ms | 7.7x | 2.7x |
| 25 | 0.0ms | 0.6ms | 0.4ms | 0.3ms | 2.3x | 1.4x |
| 50 | 0.0ms | 0.9ms | 0.6ms | 0.2ms | 4.3x | 2.8x |
| 100 | 0.0ms | 1.3ms | 1.0ms | 0.5ms | 3.0x | 2.2x |
| 200 | 0.0ms | 2.2ms | 1.7ms | 1.5ms | 1.5x | 1.1x |
| 500 | 0.0ms | 11.6ms | 8.1ms | 7.1ms | 1.6x | 1.2x |
| 1000 | 0.0ms | 44.8ms | 37.6ms | 34.7ms | 1.3x | 1.1x |
| 2000 | 0.1ms | 301.4ms | 277.3ms | 270.9ms | 1.1x | 1.0x |
| 5000 | 0.1ms | 4,383.8ms | 4,005.4ms | 3,844.6ms | 1.1x | 1.0x |
With the structured Jacobian path, CQP warm overhead decreases from 1.2x at n=500 to 1.0x at n=5,000 in this run.
1.6 MILP Scaling: Binary Knapsack

Problem: Single-constraint binary knapsack (sum(x) <= n//2)
1.6.1 VectorVariable (n ≤ 5,000)
| n | Build | Cold Solve | Warm Solve | SciPy | Cold Overhead | Warm Overhead |
|---|---|---|---|---|---|---|
| 10 | 0.1ms | 0.9ms | 0.7ms | 0.7ms | 1.4x | 1.0x |
| 25 | 0.0ms | 1.0ms | 0.8ms | 0.6ms | 1.6x | 1.2x |
| 50 | 0.0ms | 1.0ms | 1.0ms | 0.8ms | 1.3x | 1.2x |
| 100 | 0.0ms | 1.5ms | 1.3ms | 1.1ms | 1.3x | 1.2x |
| 200 | 0.0ms | 2.4ms | 2.1ms | 1.8ms | 1.3x | 1.1x |
| 500 | 0.0ms | 6.6ms | 6.1ms | 5.4ms | 1.2x | 1.1x |
| 1000 | 0.0ms | 20.0ms | 20.3ms | 18.4ms | 1.1x | 1.1x |
| 2000 | 0.0ms | 114.7ms | 63.0ms | 61.8ms | 1.9x | 1.0x |
| 5000 | 0.0ms | 414.7ms | 385.9ms | 385.8ms | 1.1x | 1.0x |
MILP timings are noisy at small sizes, including outliers at n=50 and n=100. From n=500 through n=5,000, warm overhead ranges from 1.0x to 1.1x in this run.
1.7 Overhead Summary by Problem Type

| Problem Type | Cold Overhead | Warm Overhead |
|---|---|---|
| LP (n=50) | 1.6x | 1.2x |
| LP (n=5000) | 1.0x | 1.0x |
| NLP (n=50) | 2.6x | 1.5x |
| NLP (n=5000) | 1.4x | 1.4x |
| CQP (n=50) | 4.3x | 2.8x |
| CQP (n=5000) | 1.1x | 1.0x |
| MILP (n=50) | 1.3x | 1.2x |
| MILP (n=5000) | 1.1x | 1.0x |
Pattern: The large n=5,000 LP, CQP, and MILP cases are at 1.0x warm overhead after rounding. The NLP case is 1.2x. Ratios for sub-millisecond and small solver runs vary more and should be read alongside the absolute timings.
1.8 When to Use Optyx
1.8.1 Ideal Use Cases
✅ Parameter sweeps: Solve similar problems with different parameters
✅ Real-time optimization: Repeated solves with cached structure
✅ Prototyping: Clean Python API, no manual gradients
✅ Large LP: VectorVariable reaches 1.0x warm overhead at n=5,000 in this run
✅ Non-convex NLP: Automatic differentiation with exact gradients
✅ Mixed-integer programming: MILP reaches 1.0x warm overhead at n=5,000 in this run
1.8.2 Consider Alternatives For
⚠️ One-shot problems: Cold-solve includes compilation overhead
⚠️ Large dense matrix problems (n>1000): CVXPY’s specialized solvers may scale better
⚠️ Loop-based variables at scale: Use VectorVariable instead
1.9 Running Benchmarks
# All benchmarks
uv run pytest benchmarks/ -v
# By category
uv run pytest benchmarks/validation/ -v
uv run pytest benchmarks/performance/ -v
uv run pytest benchmarks/accuracy/ -v
uv run pytest benchmarks/comparison/ -v
# Generate plots
uv run python benchmarks/run_benchmarks.py1.10 Latest Console Summary
OVERHEAD SUMMARY BY PROBLEM TYPE
================================================================================
LP n=50: Cold=1.6x, Warm=1.2x
LP n=5000: Cold=1.0x, Warm=1.0x
NLP n=50: Cold=2.6x, Warm=1.5x
NLP n=5000: Cold=1.4x, Warm=1.4x
CQP n=50: Cold=4.3x, Warm=2.8x
CQP n=5000: Cold=1.1x, Warm=1.0x
MILP n=50: Cold=1.3x, Warm=1.2x
MILP n=5000: Cold=1.1x, Warm=1.0x
Saved: benchmarks/results/overhead_breakdown.png
================================================================================