perf: avoid unnecessary coordinate array copies in hot geometric paths - #3164
Open
bscuron wants to merge 2 commits into
Open
perf: avoid unnecessary coordinate array copies in hot geometric paths#3164bscuron wants to merge 2 commits into
bscuron wants to merge 2 commits into
Conversation
getCoord() defensively copies its result on every call, a global fix for one historical mutating caller (@turf/rhumb-distance, Turfjs#2167). Added getCoordRaw(), a non-copying internal variant, and switched every call site that only reads the result (never mutates it or embeds it by reference into a returned GeoJSON object) to use it instead. distance() in particular is a dependency of 23 packages and is often called in tight per-coordinate/per-segment loops. Also removed a wrap-in-point()-then-getCoord()-unwrap round trip per segment in nearestPointOnLine, using the already-available raw coordinate array directly. Left getCoord() (copy-safe) in place at the few call sites that embed its result into a returned Feature (centerOfMass, lineSplit, shortestPath), where switching to a raw reference would alias the caller's input geometry. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VvWWffxvccsNS5ePvdDiiR
getCoord and getCoordRaw had identical validation logic, differing only in whether the result was copied. Make getCoord delegate to getCoordRaw and copy once, instead of maintaining two copies of the same checks. Measured no difference vs the extra call (V8 inlines it). Also replaces a manual [...getCoordRaw(to)] copy in rhumbDistance with getCoord(to), which is exactly what getCoord is for. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VSdmXWDQ2epJKfoGXFKdqT
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.
Summary
getCoord()defensively copies its result on every call, a global fix for one historical mutating caller (@turf/rhumb-distance, #2167). AddsgetCoordRaw(), a non-copying internal variant, and switches every call site that only reads the result (never mutates it or embeds it by reference into a returned GeoJSON object) to use it instead.distance()in particular is a dependency of 23 packages and is often called in tight per-coordinate/per-segment loops.Also removes a wrap-in-
point()-then-getCoord()-unwrap round trip per segment innearestPointOnLine, using the already-available raw coordinate array directly.getCoord()(copy-safe) is left in place at the few call sites that embed its result into a returnedFeature(centerOfMass,lineSplit,shortestPath), where switching to a raw reference would alias the caller's input geometry.Performance: this change alone
Benchmarked with each touched package's own
bench.ts(Benchmark.js), comparing the parent commit against this one, same machine, same run. Two packages (turf-boolean-point-on-line,turf-directional-mean) produce no usable output from their existingbench.tsin this environment — unrelated pre-existing issue, excluded below. Three packages (turf-ellipse,turf-great-circle,turf-line-split) initially measured as regressions in a single pass; re-measured with the run order reversed and the effect vanished, confirming it was a measurement-order artifact, not a real difference — the corrected (order-verified) numbers are used below.turf-boolean-point-in-polygonturf-distance-weightturf-distanceturf-bearingturf-planepointturf-rhumb-bearingturf-quadrat-analysisturf-nearest-point-on-lineturf-rhumb-distanceturf-rhumb-destinationturf-destinationturf-transform-scaleturf-ellipseturf-great-circleturf-line-split¹ Re-measured with run order reversed to confirm the direction/magnitude is real, not a measurement artifact.
²
turf-quadrat-analysis'snearestfixture only gets 5-9 Benchmark.js samples (each iteration takes multiple seconds), so this one has more inherent noise than the others.³ Within noise; this package's call sites weren't on a hot path materially exercised by its own benchmark fixtures.
turf-boolean-point-in-polygon's point-in-ring test does agetCoord()(nowgetCoordRaw()) per vertex in a tight ray-casting loop, so it sees the largest, most reproducible win of the set.Performance: if
polyclip-ts's optimization PR also merges@turf/union,@turf/difference,@turf/intersect,@turf/dissolve, and@turf/maskall depend onpolyclip-tsfor their core geometry engine. We also have an open performance PR againstpolyclip-tsitself (luizbarboza/polyclip-ts#26 — BigInt-backed exact arithmetic + memoized segment comparisons, +64% to +90% on its own benchmarks). Benchmarked here by swapping the currently-publishedpolyclip-ts@0.16.8for a local build of that PR's branch, same fixtures, same machine — this repo's own code (this PR'sgetCoordRawchange included) held constant on both sides:@turf/union@turf/difference@turf/intersect@turf/mask@turf/dissolveEvery fixture in every package improved (no mixed signal here, unlike the table above), and the effect size (roughly 2.2-2.65x) is far larger than any measurement noise observed elsewhere in this testing, including a reversed-order and repeat-run spot check on
@turf/mask.Test plan
bench.ts(tables above)Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01VSdmXWDQ2epJKfoGXFKdqT
🤖 Generated with Claude Code
https://claude.ai/code/session_01VSdmXWDQ2epJKfoGXFKdqT