Cutting enginePerformance

Engine performance

Real numbers from the production engine running on a c5.large-equivalent CPU (2 vCPU, 4 GB RAM).

Pack-time benchmarks

Roll sizePartsMedian solvep95 solveIterations
4 m²81.2s1.9s1,400
6 m²121.8s2.6s2,200
6 m²242.4s4.1s3,100
9 m²363.6s5.8s4,400
9 m²605.0s (capped)5.0s (capped)5,000 (cap)

Default max_solve_seconds is 5; default max_iterations is 5,000. Most production loads fit comfortably under both. For oversized batches, split into multiple jobs.

Yield benchmarks

vs. hand-planned cuts on the same job specs (matched-pair comparison, n=240 jobs over 60 days):

Material complexityHand-planned medianAuto Nest medianImprovement
Rectangular parts only78%91%+13 pts
Mixed convex shapes72%87%+15 pts
Convex + concave64%79%+15 pts
With offcut reuse on64%84%+20 pts

Offcut reuse — where the engine considers existing offcut inventory before pulling a fresh roll — accounts for ~5 percentage points of the yield improvement on average.

Cold start

pack-cli is a Rust binary, no JIT warmup. Cold start (first invocation in a new container) is ~50 ms. Subsequent invocations on the same Vercel function instance reuse the spawned process and return in ~10 ms before the solver starts.

Concurrent operators

Each Pack request spawns its own pack-cli subprocess and acquires a per-roll Postgres advisory lock. Multiple operators can pack different rolls simultaneously without contention. Two operators packing the same roll will see the second request blocked on the lock until the first commits or the 30-second timeout elapses.

In practice, advisory-lock conflicts are rare — operators tend to claim a roll, walk to it, and not start a second pack on the same stock.

Memory

Per-solve memory peaks around 80–120 MB for typical workloads. The 9 m² × 60-part stress case peaks at ~280 MB. Vercel Functions are configured with 1024 MB RAM for the pack-cli invocation.

If you see solves capped at 5 s on a complex job, the engine is leaving optimization on the table. Increase max_solve_seconds per-request via the API; production defaults to 5 to keep the operator experience snappy.

See also