Speed up triangulation, especially hole bridging (~52% faster)#195
Open
mourner wants to merge 18 commits into
Open
Speed up triangulation, especially hole bridging (~52% faster)#195mourner wants to merge 18 commits into
mourner wants to merge 18 commits into
Conversation
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.
Closes #183. Makes earcut substantially faster on its primary workload — integer-coordinate polygons from Mapbox Vector Tiles — without any API or output-contract change. On a representative 132-tile fixture (119,680 polygons, 1.9M verts, 6.8% with holes), total triangulation time drops from ~1000 ms to ~480 ms (~52% faster).
Total-time win per zoom bracket
Want the z12+ number broken down further (e.g. per individual zoom), or is the three-bracket picture enough?
What changed
Hole bridging block index.
findHoleBridgepreviously ran two unindexed O(n) scans of the merged ring per hole — 41.7% of total time. It now skip-walks an append-only block index (flat per-block bounding boxes, sized once and reused across calls, no per-call allocation):Combined, this took
findHoleBridgefrom 41.7% → 11.8% of self-time and the holey-polygon bucket from ~948 ms → ~590 ms. Block size K=16 confirmed optimal by sweep.intersectsPolygondiagonal prune. The split fallback's diagonal validity check now skips edges whose bbox can't overlap the (short) diagonal — bit-identical output, ~3% speedup.z-order sort: linked-list merge sort → array quicksort. The cache-hostile pointer-chasing
sortLinkedis replaced with a cache-friendly array quicksort over node refs. Byte-identical output, removes code, ~2.5% speedup.microoptimized earcutLinked: hoisted cheap reflex vertex check out of
isEar/isEarHashedso that a common path doesn't call non-inlined functions. ~6% speedup.microoptimized isEarHashed: inlined point in triangle checks in
isEarHashed— the calls weren't inline due to being over inlining budget. ~2% speedup.Faster hole merging & point filtering.
filterPointsnow heals only the dirty window around each bridge/diagonal cut (O(window)) instead of re-walking the whole ring per removal (was quadratic on holey polygons), with one upfront full-ring collapse after hole elimination to preserve global cleanup. ~4%Dropped the "skip next vertex" sliver heuristic. It cost ~10% (+55% inner-loop iterations to relocate ears) for no quality benefit on real data — triangle shape is irrelevant to GPU fill rendering. ~5%. Adjusts a few canonical fixture outputs and loosens some tolerances on invalid/degenerate-prone polygons.
Minor cleanup.
cureLocalIntersectionsskips its trailing full-ringfilterPointswhen it cured nothing (a guaranteed no-op on already-filtered rings). Removedsteinerfield from the Node shape since this is a rare path that shouldn't bring the cost to the common one.