Skip to content

perf(rect_clip): skip segments beyond one edge, don't load large polygons twice - #978

Open
dktue wants to merge 1 commit into
systemed:masterfrom
dktue:perf/rect-clip-big-polygons
Open

dktue wants to merge 1 commit into
systemed:masterfrom
dktue:perf/rect-clip-big-polygons

Conversation

@dktue

@dktue dktue commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Gain: profiles with many huge polygons get 24% faster (−26% CPU): a parks profile on Alaska/BC/Yukon goes from 275.6 to 210.2 s, recovering about 70% of what #949 cost it. Planet (OpenMapTiles): wall time unchanged, 4.5% less CPU. Output identical.

#949 made this profile 52% slower: cldellow's parks slice is almost entirely huge forest and park polygons. A bisect on Alaska/BC/Yukon (16 threads, 2 rounds) put the step exactly at #949:

master at wall user CPU
after #943/#945, before #949 (5b9c61f) 182.0 / 179.4 s 2,498 / 2,520 s
with #949 (bfe7a2f) 276.2 / 278.5 s 4,013 / 4,033 s

The profile showed two costs, both proportional to the size of the whole polygon, paid for every tile it covers:

  • rect_clip ran the full Liang-Barsky computation for every segment (four divisions plus branching), also for segments far away from the tile. For a polygon with hundreds of thousands of points in a z14 tile, that is almost all of them. clipSegment was 91% of rect_clip's time.
  • The polygon was loaded from the store a second time. The first copy was moved into fast_clip, which modifies it, so rect_clip needed the original again.

This PR fixes both:

  • clipRing rejects segments beyond one edge first. If both end points lie beyond the same edge of the box (both left, right, above or below), the segment can't reach the box: four comparisons instead of the clip computation.
  • A large valid polygon is kept for rect_clip instead of loaded again. For polygons over 1,000 points known to be valid, fast_clip works on a copy. Smaller polygons keep the existing path, so they don't pay for a copy they rarely need.

Profile, parks on Alaska/BC/Yukon, 16 threads (CPU samples)

before #949 with #949 this PR
total 259,450 414,767 304,834
populateMultiPolygon 55,866 112,052 57,531
rect_clip – 106,454 33,517

Results, 16 threads, 2 rounds each

master (bfe7a2f) this PR
parks, Alaska/BC/Yukon, wall 275.8 / 275.4 s 209.4 / 211.1 s
user CPU 4,006 / 4,002 s 2,973 / 2,970 s

Planet, OpenMapTiles profile, .pmtiles, 32 threads, --store --fast (one run each)

master (bfe7a2f) this PR
wall 2,342 s 2,346 s
user CPU 57,858 s 55,232 s (−4.5%)
peak anon memory 25.9 GB 25.9 GB

On the planet the OpenMapTiles profile has few polygons big enough to matter here, so the wall time doesn't change. The output was 48 MB (0.06%) smaller; planet runs differ slightly between identical runs, and the Germany and parks comparisons below are identical.

Output

The remaining distance to the pre-#949 time comes from clipping the whole polygon for every tile. Since #939, the clip cache is per batch, so huge polygons are rarely clipped from a smaller cached piece; that also costs this profile about 2.8× compared with before #939. A deterministic shared cache is a separate, larger change.

🤖 Generated with Claude Code

…gons twice

systemed#949 made profiles with many huge polygons 52% slower: rect_clip ran the
Liang-Barsky computation for every segment of the whole polygon on every
tile, and the polygon was loaded from the store a second time because
fast_clip had modified the first copy.

- clipRing rejects a segment whose end points both lie beyond the same
  box edge with four comparisons, before the clip computation
- a valid polygon over 1,000 points is kept for rect_clip; fast_clip works
  on a copy instead of the moved original

parks profile, Alaska/BC/Yukon, 16 threads: 275.6 -> 210.2 s (-24%),
CPU -26%. Output identical (parks extract, Germany OpenMapTiles).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant