Repository navigation
Perf/arc floodfill cache - #963
Conversation
The `--benchmark` flag previously printed whole seconds, which hides sub-second differences when evaluating micro-optimizations on small bboxes. Switching to milliseconds keeps the same printed format but exposes the signal needed to reason about changes that shave tens or hundreds of ms off a run.
`FloodFillCache::get_or_compute` previously deep-cloned the cached
`Vec<(i32, i32)>` on every call. For dense forests or farmland polygons
these vecs can hold 10k-100k+ coordinates, so each handler invocation
copied tens to hundreds of kilobytes. The comment on the old clone even
acknowledged the tradeoff ("the cost is acceptable vs Arc complexity").
Wrapping the cached vecs in `Arc<Vec<_>>` replaces the copy with a
refcount bump and removes the transient duplicate allocation. For
Node/Relation fallbacks the empty result is served from a process-wide
`OnceLock` sentinel so the cold path doesn't regress either.
Call sites were updated mechanically:
- Move iterators (`for x in vec`) -> borrowing iterators (`for x in vec.iter()`)
- `for x in &vec` -> `for x in vec.iter()` (Arc derefs through method calls
but not through `&` IntoIterator)
- buildings.rs still materializes an owned Vec because the hole-carving
path needs `retain`; the deep copy there is cheap (building footprints
are small) and keeps the rest of the function untouched.
Wall-clock impact on tested bboxes (Berlin/Potsdam/Grunewald) is within
measurement noise (<0.5%) — the dominant generation cost is block
placement, not cache fetch — but allocation pressure and peak memory
drop, which matters more as area size and polygon count grow. Tests
(65/65), clippy -D warnings, and fmt are clean.
The `--benchmark` flag now prints `generation_time_ms=` instead of `generation_time=`; the workflow's grep was still looking for the old key and would silently drop `gen_time` on every PR run. Parse the ms value, round to the nearest whole second, and feed that into the rest of the seconds-scale comparison logic so the verdict and PR comment keep working unchanged.
|
⏱️ Benchmark run finished in 1m 8s 📈 Compared against baseline: 20s 🚨 This PR drastically worsens generation time. 📅 Last benchmark: 2026-04-22 11:18:31 UTC You can retrigger the benchmark by commenting |
|
Craftsman |
There was a problem hiding this comment.
Pull request overview
This PR improves world-generation performance by avoiding deep clones of large flood-fill coordinate lists, switching the flood-fill cache to share results via Arc, and updates the PR benchmark plumbing to report generation time in milliseconds.
Changes:
- Store flood-fill cache entries as
Arc<Vec<(i32, i32)>>and return cheapArcclones instead of deep-copyingVecs. - Update element processors to iterate over cached results by reference (and explicitly materialize a
Veconly where mutation is needed). - Emit
generation_time_msfrom generation code and update the PR benchmark workflow to parse/round it back to seconds for reporting.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/floodfill_cache.rs |
Changes cache value type to Arc<Vec<_>>, adds shared empty sentinel, updates internal iteration to account for Arc. |
src/element_processing/natural.rs |
Adapts to Arc flood-fill results and iterates without consuming/cloning. |
src/element_processing/leisure.rs |
Adapts to Arc flood-fill results and iterates without consuming/cloning. |
src/element_processing/landuse.rs |
Adapts to Arc flood-fill results and iterates without consuming/cloning. |
src/element_processing/historic.rs |
Adapts to Arc flood-fill results and iterates without consuming/cloning. |
src/element_processing/highways.rs |
Adapts to Arc flood-fill results and iterates without consuming/cloning. |
src/element_processing/buildings.rs |
Clones into a Vec only where ownership/mutation is required; updates loops accordingly. |
src/element_processing/amenities.rs |
Adapts to Arc flood-fill results; updates iteration sites. |
src/data_processing.rs |
Switches benchmark emission from seconds to milliseconds (generation_time_ms). |
.github/workflows/pr-benchmark.yml |
Parses generation_time_ms and rounds to seconds for existing report formatting/thresholds. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Both the precompute path and the on-demand fallback were wrapping `flood_fill_area`'s output in a fresh `Arc::new(...)`, so empty results (degenerate rings, timeouts) created distinct small allocations instead of reusing the `empty_flood_fill_result()` sentinel introduced earlier in the PR. Consolidating both code paths so empty results always point at the shared Arc removes the inconsistency and saves ~40 bytes per empty cache entry. Addresses Copilot review comments on the PR.
|
retrigger-benchmark |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
No description provided.