Repository navigation
perf: keep each surface's boundary curves for CoincidenceCheck (outputs bit-identical) - #1031
Merged
Merged
Conversation
…ts bit-identical) Not upstream: SmSurface::CoincidenceCheck makes both surfaces' natural boundary curves again for every check, and a boolean runs it for every face pair. Each Surface value now keeps them in a memo (BoundaryMemo, a OnceLock), keyed by the domain's bits and the kernel inputs of the build. The check borrows the curves, and the curves carry their curve cache memos, so each boundary cache is built once per surface value. Only B-spline, plane, cone, sphere and torus surfaces are memoised. A copy starts with an empty memo; an edit through &mut Surface replaces it. Kernel::memoize_curve_caches and the curve-memo-off feature turn it off with the curve cache memo. tangency-labs#948 loft, 20 sections: 128 curve-cache builds in CoincidenceCheck (C++ 600), 937 in the whole run (C++ 1393). Whole-run instructions: 57.3 G -> 41.9 G at 20 sections, 179.1 G -> 107.3 G at 40 (C++ 41.7 G, 127.8 G). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 25, 2026
meros
force-pushed
the
perf/memo-boundary-curves
branch
from
October 26, 2026 04:07
dd47b13 to
2f2d231
Compare
meros
marked this pull request as ready for review
October 26, 2026 04:07
This was referenced Oct 26, 2026
bherbruck
approved these changes
Oct 26, 2026
bherbruck
left a comment
Collaborator
There was a problem hiding this comment.
Approved by the automated first-pass review: CI green, mergeable, no CI/build/dependency file changes, no red-flag patterns in added code (process/network/env/unsafe outside FFI/blobs/hidden unicode), 0 known-mismatch entries added. Logic is gated by CI and the difftest merge queue.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This goes beyond upstream. The C++ makes the natural boundary curves of both surfaces again for every coincidence check, and a boolean runs that check for every face pair. This PR keeps those curves in each surface, so their curve caches are built once per surface. On the #948 loft at 40 sections the whole run now takes 94.0 G instructions, against 164.4 G on main and 127.8 G in the C++. No output changes, with the memo on or off.
Cause
SmSurface::CoincidenceCheck(SmSurface.cpp:7553-7580) callsCreateNaturalUVTrimCurveson both surfaces and deletes the curves at the end (SmObjsDelete). TheirSmCacheMgrentries go with them. The port does the same, so with the curve cache memo (#992) the build count there equals the C++ count: 600 curve-cache builds at 20 sections (gdb onTessellate). Each check rebuilds the same curves for the same surface. At 40 sections the check was 30 % of the C++ samples.Change
Surfacehas a private fieldboundary_memo: BoundaryMemo, aOnceLockwith one entry: the result oftrim_curves(CreateNaturalUVTrimCurves) and its key.CurveMemoKey::of_kernel, the same tolerances and context scalars as the curve memo of perf: memoise curve caches in the curve value (outputs bit-identical) #992. A check with another key builds the curves and owns them, and it does not replace the entry.CreateIsoParametricCurveoverrides read nothing but the surface's own value. Offset, curve-bounded, step, volume, revolved and extruded surfaces always build.base_coincidence_checkborrows the curves (Cow::Borrowed). It only reads them, so nothing is copied. The curves carry their own curve cache memos (perf: memoise curve caches in the curve value (outputs bit-identical) #992), so each boundary curve's cache is built once per surface value.m_pCacheObjnull.kind_mutand the 6 editing methods that writeself.kindreplace the memo.Debugprints it as a constant.Kernel::memoize_curve_cachesand thecurve-memo-offfeature (both from perf: memoise curve caches in the curve value (outputs bit-identical) #992) turn this memo off too.surface.surface_p8describes the memo. The finding is inplan/findings/surface.surface_p8--coincidence-boundary-memo.md.Why the memo cannot change a result
CreateNaturalUVTrimCurvestakes&Kerneland the surface, and for the memoised kinds it reads only the surface's kind. Its result is a function of the surface value, the domain and the key, so a hit gives what a fresh build gives. AOnceLockfills once and is never changed afterwards, so the order in which checks run cannot matter.unsafe.SurfacestaysSend.Test plan
Curve-cache builds per caller. gdb on
Tessellate, 20 sections, the method of #992. OnlyCoincidenceCheckchanges. The counts were taken on the same change before the rebase (on #992's1a2f705dd); the rebase changed no code of this PR.Instructions. The #948 skins,
examples/toebox_loft.rs(local only). Release builds of maincf4dd475a(with #992) and of this branch, pinned to the 4 performance cores, 3 runs each, interleaved with the C++ (c9c5979). User-space instructions for the whole run (skins,outer - inner,∩ box, volumes); they vary by less than 0.1 %.outer - innertook 5.15 s against 9.49 s with perf: memoise curve caches in the curve value (outputs bit-identical) #992 and 5.65 s in the C++ at 40 sections (median of 3). I did not time it again after the rebase, because the machine was running the checks.CoincidenceCheckat 40 sections, from a perf profile before the rebase (share × samples ÷ rate): Rust 7.2 % of 9,247 samples at 999 Hz, about 0.66 s; C++ 30.0 % of 3,056 samples at 499 Hz, about 1.84 s.Bit identity.
boundary_memo_hit_is_the_fresh_build. Two overlapping B-spline patches give the sameCoincidencewith the memo off, on (first fill) and on again (hit). A copy starts empty,kind_mutempties the memo, and other tolerances miss and give the fresh result.tests/curve_cache_memo.rshas two new cases with analytic and B-spline surfaces (skin - cylinder,skin ∩ sphere), so 8 booleans run with the memo off, on and on again. It compares every output value bit for bit, with the default features and withcurve-memo-off.Checks.
cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warnings,cargo clippy -p solidcore --all-targets --features curve-memo-off -- -D warningsandcargo check -p solidcore --target wasm32-unknown-unknownpass.cargo test -p solidcorepasses: 2,233 tests, 0 failed.tests/curve_cache_memo.rswith--features curve-memo-offpasses.--uniton the same 31 units as perf: memoise curve caches in the curve value (outputs bit-identical) #992 (surface.surface_p8: 2108 cases), in both positions (--features cpp, and--features cpp,solidcore/curve-memo-off): 0 failed andmax_rel_err=0e0in each. The summary lines are the same in both positions.Not covered
--greendifftest run (CI runs it).Related