This repository was archived by the owner on Oct 28, 2026. It is now read-only.
Repository navigation
Skip the intersection merge sort when no edges cross in a scanbeam - #1116
Open
xaviergonz wants to merge 1 commit into
Open
xaviergonz wants to merge 1 commit into
xaviergonz wants to merge 1 commit into
Conversation
BuildIntersectList's bottom-up merge sort always makes about log2(n) passes over the active edge list, even when no edges cross in the scanbeam, which is the usual case. AdjustCurrXAndCopyToSEL now also checks whether the edges are still in order, and if so there's nothing to find, so the merge sort is skipped. In that case every edge's curr_x is already TopX(top_y), so DoTopOfScanbeam doesn't recompute it either. Output is unchanged. Done in all three ports.
xaviergonz
force-pushed
the
perf/sorted-ael-fast-path
branch
from
October 14, 2026 22:31
82aeff8 to
b2d92ae
Compare
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Hi Angus,
This one came out of the same profiling session as #1115, but it's the bigger win.
In practice most scanbeams have no intersections at all, but
BuildIntersectListstill runs the full bottom-up merge sort over the active edges every time. Being bottom-up, it does about log2(n) complete passes over the list even when the list is already sorted. With a few hundred to a thousand active edges, that's around 10 passes per scanbeam just to find out nothing crossed. (Clipper1's bubble sort happened to bail out after a single pass in that case.)So the change is small:
AdjustCurrXAndCopyToSELalready walks every edge to computecurr_x. It now also checks whether the edges are still in order, and returns that.BuildIntersectListreturns false straight away. The merge sort only records an intersection (or moves an edge) whenright->curr_x < left->curr_x, so on a sorted list it would do nothing anyway.curr_xis alreadyTopX(top_y), since nothing modified it afterwards. SoDoTopOfScanbeamskips recomputing it for edges that aren't at their top. When there are intersections everything runs exactly as before.Done the same way in C++, C# and Delphi.
Numbers
These are unions (NonZero) of overlapping noisy circular blobs ("64×256" = 64 blobs of 256 vertices), on an M1 Max. Times are ms per operation, best of 5.
I also tried it in a WebAssembly build (emscripten), where it's roughly 1.5–2.1× faster. That's actually what got me looking. Without it, Clipper2 compiled to wasm was a bit slower than Clipper1, even though it's faster natively.
Testing
The C++ test suite passes (96/96), and so do the C# Tests1 and TestsZ projects.
I also ran a differential fuzz test against
mainin all three ports. It does 200k random cases with tiny coordinate ranges, to force collinear points, horizontals and duplicates. The cases cover:It hashes every output, and the hashes are identical to
main's in every port. As a sanity check, deliberately breaking theDoTopOfScanbeampart does change the hash, so the test is actually sensitive to this code.It doesn't overlap with #1115 (different functions), so they can go in in either order.
Thanks for Clipper2, by the way. Happy to adjust anything.