Files
OpenNest/docs/performance/fill-verification.md
T
aj 1b23ad79f2 perf(fill): reuse part triangulations within an overlap check
After 1b, triangulating both polygons on every pair was the largest
remaining overlap cost (27% of main-thread samples on the corpus job).
PartOverlapChecker now triangulates each part at most once per check,
lazily after the bounding-box gate, and passes the triangles to a new
internal Collision.HasOverlap overload that runs the unchanged
OverlapRegions body. Triangles are only read by clipping and hole
subtraction, so reuse gives identical verdicts.

Verification:
- 49,000 seeded decisions with reused triangles match LegacyCollision;
  triangles stay bit-identical to a fresh triangulation afterwards.
- Debug PolygonTriangulations: 246 -> 40 and 64 -> 36 per grid check;
  sharing triangles per Program instead fails 23 tests.
- Corpus job (169 parts, --engines Default --parallel 1): median
  13,398 -> 12,702 ms over 4+4 alternating runs vs 1b, identical
  outcomes; serialized layout byte-identical to the base.

Also records the Follow-up B' (Slices 1a, 1b, 2a) measurements in
docs/performance/fill-performance.md.
2026-09-27 13:49:46 -04:00

3.9 KiB

Fill performance verification

Opt-in synthetic measurements (OpenNest.Tests/Fill/FillPerformanceTests.cs):

OPENNEST_RUN_FILL_PERF=1 dotnet test OpenNest.Tests/OpenNest.Tests.csproj -c Release \
  --filter 'Category=FillPerformance' --logger 'console;verbosity=detailed'

Only the exact value 1 enables these tests; otherwise they skip; README documents the PowerShell equivalent.

The category covers comparer, group-pattern, rotated-pattern, extents-column, feature-extraction, no-model angle, and FillLinear offset-geometry workloads; individual filters match benchmark method names in FillPerformanceTests.cs. Overlap checks are measured separately by OverlapCheck_ReportsPolygonPairsAndGridChecks in OpenNest.Tests/Fill/OverlapCheckPerformanceTests.cs (same category). Keep harness, inputs, warmups and batches identical before/after; exclude setup/assertions from timing. Comparer/extents allocations are synchronous and current-thread only; parallel group fills omit allocation totals. No timing CI gates or whole-job speedup claims. Preserve evidence in the measured report.

Debug behavior/skipped-work checks:

dotnet test OpenNest.Tests/OpenNest.Tests.csproj -c Debug \
  --filter 'FullyQualifiedName~DefaultFillComparerWorkTests|FullyQualifiedName~FillHelpersTests|FullyQualifiedName~FillExtentsTests|FullyQualifiedName~StrategyOverlapTests|FullyQualifiedName~FillLinearGeometryReuseTests|FullyQualifiedName~CollisionOverlapOnlyTests|FullyQualifiedName~PartOverlapCheckerTests'

PerfCounters.FillScoreComputations, PartBoundaryPreparations, PartBoundsUpdates, OffsetPerimeterEntities, FeatureBitmaskCells, CrossingPointScans, OverlapPolygonPreparations, and PolygonTriangulations increments compile away in Release: zero Release counters prove nothing. OverlapPolygonPreparations counts overlap-preparation starts (material extraction), not completed polygons: Part.Intersects counts both parts on every call, PartOverlapChecker counts once per distinct Program. PolygonTriangulations counts Collision triangulations; the checker triangulates a part at most once per check, and only after a bounding-box hit. Serialize counter assertions in FillCacheCollection and reset in finally. Keep OpenNest.Tests/Fill/LegacyFillExtents.cs, OpenNest.Tests/Fill/LegacyFillLinear.cs, OpenNest.Tests/Geometry/LegacyCollision.cs, and OpenNest.Tests/Fill/LegacyPartOverlap.cs frozen for differential tests (never route them through production helpers), not production or before timings; measure the actual baseline production code.

Task 4b checks: dotnet test OpenNest.Tests/OpenNest.Tests.csproj -c Release --filter "FullyQualifiedName~AngleCandidateBuilderTests|FullyQualifiedName~AnglePredictorTests|FullyQualifiedName~FeatureExtractorTests" (repeat in Debug for bitmap counters). IrregularAngles_ReportsWarmNoModelPath measures the public builder with a missing model and skips when a model is installed; never remove real model files to benchmark. FeatureExtraction_ReportsFullAndScalarOnly measures extraction separately.

Predictor availability uses the same one-attempt session initialization as inference. Publish completion only after assignment or definitive failure; concurrent callers must wait for the outcome. The builder skips extraction when unavailable and requests scalar-only features when available. Tests use isolated loaders/prediction doubles, not evidence of real ONNX inference.

Whole-job before/after comparisons use OpenNest.Benchmark with a *.manifest.json corpus and --parallel 1 (see the report's Task 5 section for the delivered real-DXF manifest, hashes, outcome confirmation, and inconclusive whole-job timing). Circle-heavy archive drawings can validate INVALID at spacing even on the pre-batch baseline, and larger quantities can crash both trees identically; record such pre-existing behaviors instead of treating them as regressions or tuning around them.