Repository navigation
Conversation
dktue
force-pushed
the
perf/precheck-index
branch
from
October 31, 2026 22:41
adbf4a4 to
7afff5e
Compare
This was referenced Oct 31, 2026
…lygons once The validity precheck located every hole by walking the whole outer ring and tested hole nesting pairwise, both capped at 2^26 vertex visits. Huge ocean polygons with thousands of islands exceeded the caps and fell back to boost::geometry::is_valid, twice (processShapeGeometry and again in ShpMemTiles::StoreGeometry), which made the planet shapefile phase last as long as its slowest polygon (91 s). - holesClearlyPlaced: bucket all ring segments into horizontal bands and cast one ray per hole, counting crossing parity per ring; no work cap - rayClearlyCrosses: exact half-open y comparisons, so rays across large polygons are no longer "unsure" at nearby vertices - shapefile polygons that are valid and inside the clip box skip the clip and pass knownValid to StoreGeometry Planet shapefile phase 95 s -> 21 s (-78%), total wall -3.9%; precheck re-verified against is_valid on planet shapefiles and Germany with 0 mismatches.
dktue
force-pushed
the
perf/precheck-index
branch
from
November 1, 2026 21:00
7afff5e to
ffd911d
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 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.
Gain (planet, PMTiles, 32 threads, one run each): shapefile phase 95 s → 21 s (−78%); total wall 1855 → 1784 s (−3.9%), user CPU −5.7%, memory unchanged. Output geometry unchanged.
Follow-up to #961 (merged). On the planet, loading the ocean shapefile (
water_polygons.shp, 53,324 records) takes about 95 s, and for most of that time only a few of the 32 threads are busy. Handing the records to the thread pool takes 4.6 s; the rest is waiting for a few dozen huge ocean polygons. Each has thousands of islands as holes, and the slowest (record 1123, 4,085 holes) takes 91 s on its own, so the phase lasts as long as that one polygon.All of these polygons are valid. Their time goes into proving it:
boost::geometry::is_validinprocessShapeGeometry(~70%), and a secondis_validon the clipped copy inShpMemTiles::StoreGeometry(~25%). The cheap precheck from #961 gives up on them because of its work caps. It locates each hole by walking the whole outer ring, which is O(holes × vertices), so it bails out above 2^26 vertex visits. It also checks for nested holes pairwise, with a similar cap.This PR changes two things.
Precheck without work caps (
src/geom.cpp). After the existing test that no two rings touch, the segments of all rings of the polygon are bucketed into horizontal bands. For each hole, a ray from one of its vertices is tested only against the segments in that vertex's band, counting crossings per ring. Odd parity for the outer ring and even for every other hole means the hole is inside the outer ring and not nested. That replaces both the per-hole outer-ring walk and the pairwise hole sweep, in near-linear time. Whether a vertex is above the ray is now decided with exact comparisons (the half-open rule), so the crossing parity is exact; only the side test near a segment keeps its tolerance and answers "unsure". With the old tolerant y-test, a ray across a big ocean polygon almost always met some vertex within the tolerance of its latitude and gave up. As before, any "unsure" falls back to Boost.No second validity check for shapefile polygons (
src/shp_processor.cpp,src/shp_mem_tiles.cpp).processShapeGeometryalready knows whether a polygon is valid. When it is valid and its envelope lies inside the clip box (every polygon on a planet run), it skips the clip and passesknownValidtoStoreGeometry, which then skips its own check. Polygons that are clipped or were repaired bymake_validare checked as before.A new unit test,
test_precheck_large_polygon, covers a polygon above the old cap (40,000-vertex outer ring, 3,600 holes) plus a hole outside the outer ring and a nested hole. It fails with the old precheck and passes with the new one.Shapefile phase on the planet (32 threads, run stopped before reading the .pbf)
The remaining 21 s are 35 valid polygons that the precheck still rejects as nearly degenerate (near-spikes, near-duplicate points, near-touching segments). Clearing them would mean loosening the precheck's tolerances, so they are left to Boost.
Planet, PMTiles, 32 threads,
--store --fastAll of the gain comes before the tile phase, where the shapefiles load. The output is 14 MB smaller because shapefile polygons inside the clip box are now stored as read instead of going through
boost::geometry::intersection, which changes ring start points and vertex order but not the geometry (see the Germany comparison below).Correctness
boost::geometry::is_valid. Planet shapefiles: 65,637 accepted, 0 disagreements. Full Germany run (OSM multipolygons in the tile phase too): 63.4 million accepted, 0 disagreements.poidiffers, which already differs between identical runs.Baseline for all measurements: master + #961 + #966 + #968 + the
correct.hppring-combine fix (separate PR).🤖 Generated with Claude Code