Files
OpenNest/docs/performance/engine-benchmarking.md
T
aj 561f5be06c feat(engine): choose which edge strips count as offcut
Equal-cost plans break ties by the largest edge offcut, from any of the
four edges. A caller that wants the leftover off one way, such as a
shop that keeps only end drops, or a benchmark scoring the used sheet
height, had no way to say so. On the converted Nestbolt jobs the
extended Irregular search then found plans whose larger offcut is a
strip down the side, which Nestbolt scores as worse: b-blaz1-strip
3.601 -> 3.867, b-shirts-strip 2.674 -> 2.995.

NestJobOptions.OffcutCut (a CutOffAxis, default null) counts only the
strips one cut along that axis frees, for salvage credit and the
compactness tie-break alike: Horizontal the full-width bottom and top
strips, Vertical the full-length sides. PlateRemnant.LargestOffcut takes
the axis; NestJobCost, the time limit and clamp-removal recovery pass
it on. Unset, nothing changes.

Benchmark manifests accept "offcutCut": "horizontal" or "vertical".
With horizontal, one CPU, 20 s limit, Default:
  b-blaz1-strip  3.934 -> 3.333
  b-shirts-strip 2.995 -> 2.610
  m-box-deep     1.918 -> 1.276 (Nestbolt 1.251)

Manifests accept only the words horizontal or vertical (any case); a
list such as "horizontal,vertical" is rejected. PlateRemnant rejects an
undefined axis. Fixed strategies keep their own no-salvage tie-break
and ignore the option; desktop, Console and MCP do not offer it yet.

Mutation reds: 14/14 (offcut-mutations.py).
2026-10-31 13:29:50 -04:00

2.6 KiB

Benchmarking whole-job engines

OpenNest.Benchmark runs each registered INestingEngine against .nest files or JSON manifests of DXFs and quantities. It ranks complete jobs first, then salvage-credited sheet cost with a penalty for each unplaced part. Invalid runs (bounds, spacing, quantity, rotation, or stock mismatch) place nothing and pay the penalty.

dotnet run --project OpenNest.Benchmark -- ./benchmark-jobs \
  --sheet-sizes 48x96,60x120,72x120 --engines Irregular,Rectangles --csv results.csv

Supply --sheet-sizes for an independent candidate pool; otherwise a saved nest offers only its original sheet sizes, which can bias comparisons. A saved nest's first plate also supplies spacing, quadrant and clamps: its machine clamp rail is resolved for every candidate size, as desktop Auto Nest does, and its baseline layout is checked against the clamps it was made with. Manifests have no clamps. A manifest part rotates freely (automatic rotation: any angle) unless "allowRotation": false locks it at 0°, or "rotations" lists its only angles in degrees: [0], [0, 180] or [0, 90, 180, 270]. A manifest's "offcutCut": "horizontal" (or "vertical") sets NestJobOptions.OffcutCut, so only full-width (or full-length) edge strips count as offcut; use it to match a benchmark that scores used sheet height (or length). A saved nest records its salvage rate, but salvage credit applies only with a positive --min-salvage-dimension. Without it, every complete one-sheet layout costs the same, as in desktop Auto Nest on a single plate size. The default --parallel 3 runs solves concurrently and confounds per-engine timing; use --parallel 1 for timing comparisons. --progress logs each solve's start, engine progress, and finish. --time-limit <seconds> stops each solve at that wall-clock limit and scores the best valid plan the engine had reported (see time limits); a solve with none counts as timed out. Without a limit the harness abandons a solve after five minutes; with one it waits for the limit plus a minute, so the engine's limit always fires first. Retain input hashes, exact commands, validity, fulfillment, costs, and raw results outside source control when comparing revisions; see fill performance verification.

Custom engines are DLLs implementing INestingEngine with a public parameterless constructor, loaded from Engines/ beside the benchmark executable. See nesting engines for built-ins and engine-change rules. Run dotnet run --project OpenNest.Benchmark -- --help for all options.