Route Boolean3::Result timing through the shared phase mechanism#1745
Route Boolean3::Result timing through the shared phase mechanism#1745zmerlynn wants to merge 2 commits into
Conversation
Per Emmett's elalish#1742 review: drop the four hand-placed Result Timers (Assembly/Triangulation/Simplification/Sorting) and report through the same per-phase timing as LevelSet/FromMesh/Smooth, so all phased ops emit the one `phase N (file:line) = X ms` format. The phase() lambda already brackets those spans (the 4 named regions were just unions of consecutive phase boundaries), so it gains a `line` arg and records via a new MANIFOLD_RECORD_PHASE_AT - an explicit-line variant, since a lambda called from 11 sites has only its own body line. Boolean never hits the static-factory reset that seeds `lastPhase`, so BeginPhaseTiming stamps the baseline at Result entry. Left alone: the intersection-stage Timers in the Boolean3 ctor (a P/Q sub-op breakdown, not sequential phases) and the verts/tris summary. perfPhases now drives its booleans through an ExecutionContext so the phase lines print. Progress/cancel semantics and the release build are unchanged (timing only records with a ctx attached, under MANIFOLD_DEBUG/_TIMING). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1745 +/- ##
==========================================
- Coverage 94.93% 94.89% -0.05%
==========================================
Files 37 37
Lines 8377 8377
==========================================
- Hits 7953 7949 -4
- Misses 424 428 +4 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| result.NumTri(); | ||
| ExecutionContext ctx; | ||
| Manifold result = op.apply(a, b).WithContext(ctx); | ||
| result.Status(); // force evaluation under ctx so phases time and print |
There was a problem hiding this comment.
What will happen if a and b are themselves the result of a tree of boolean operations and everything gets evaluated in parallel at Status? What will the timing prints look like?
There was a problem hiding this comment.
Good catch - it was a real bug. The timing baseline lived on the shared ExecutionContext, so parallel Result calls clobbered each other's deltas and the lines interleaved unattributably.
Fixed: the baseline is now a per-Result stack local, and each line carries a per-boolean uid. Still interleaved (eval is parallel), but each boolean's deltas are correct and splittable by tag:
[b0] phase 7 = 0.026 ms
[b1] phase 1 = 0.001 ms
[b0] phase 8 = 0.228 ms <- since b0's phase 7, not b1's
[b1] phase 2 = 0.017 ms
grep '\[b0\]' recovers b0's clean 1..11. Static factories (single-threaded) are unchanged. Verified race-free under TSan.
Addresses the elalish#1745 review: the per-phase timing keyed off the shared `ctx->lastPhase`, so when a CSG tree evaluates booleans concurrently the parallel `Boolean3::Result` calls clobbered each other's baseline (garbage deltas) and their phase lines interleaved with no way to tell them apart. Move the baseline to a per-`Result` stack local (`LocalPhaseTiming`) and tag every printed line with a per-boolean uid (`[bN]`) - so concurrent booleans keep correct deltas and the interleaved output is splittable. The static factories keep the ctx baseline (single-threaded, unaffected). Debug-only (verbose >= 2, MANIFOLD_DEBUG/_TIMING); release is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to #1742, addressing the review ask: reuse the new per-phase timing to drop Boolean's hand-placed Timers, for consistency.
Result's four Timers (Assembly/Triangulation/Simplification/Sorting) bracketed spans that were already unions ofphase()boundaries, so thephase()lambda now records through the same path as theADVANCE_PHASE_OR_RETURNops, via a new explicit-lineMANIFOLD_RECORD_PHASE_AT(a lambda called from 11 sites would otherwise report a single line).BeginPhaseTimingseeds the baseline atResultentry, since Boolean never hits the static-factory reset that seeds it for LevelSet/FromMesh/Smooth.perfPhasesnow drives its booleans through anExecutionContextso the lines print.Every phased op now emits the one
phase N (file:line) = X msformat. The intersection-stage Timers are kept; the 11 phase lines replace the old four named Timers:Notes:
Boolean3ctor (Intersect12 P->Q / Q->P, Winding03 P / Q): a P/Q sub-op breakdown, not sequential phases.Progress/cancel semantics and the release build are unchanged - timing only records with a ctx attached, under
MANIFOLD_DEBUG/_TIMING.