Guard cost_distance numpy dask chunk path with _check_memory (#3343)#3346
Merged
brendancol merged 2 commits intoJun 15, 2026
Merged
Conversation
The numpy map_overlap chunk function called _cost_distance_kernel directly, allocating several height*width arrays per chunk without the _check_memory guard that _cost_distance_numpy and the cupy/iterative paths apply. An oversized chunk could exhaust a worker with an opaque allocator error instead of a MemoryError pointing at max_cost= or smaller chunks. Add _check_memory(h, w) inside the chunk closure so every numpy allocation path is guarded consistently.
brendancol
commented
Jun 15, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
PR Review: Guard cost_distance numpy dask chunk path with _check_memory (#3343)
Blockers
None.
Suggestions
None.
Nits
None.
What looks good
- The fix is one line in the right place.
_make_chunk_func's closure receivessource_block, whose shape already includes the map_overlap depth, so_check_memory(h, w)measures the actual per-chunk allocation the kernel is about to make. - Backend parity is the point of the change: numpy direct (
_cost_distance_numpy), cupy/dask+cupy (_check_gpu_memory), and the iterative tile path all guarded this already. This was the lone gap. _check_memoryis a no-op for normal chunks (it only raises above 50% of available RAM), so well-sized dask graphs are unaffected.- The new test forces the map_overlap path on purpose (finite
max_cost, chunk size larger than the overlap pad), mocks memory low, and asserts theMemoryErrorsurfaces at compute time. I confirmed it fails onmain(DID NOT RAISE) and passes with the fix, so it actually pins the bug.
Checklist
- Algorithm unchanged; only a pre-allocation guard added
- Backends consistent (numpy direct + dask numpy chunk now both guarded)
- NaN handling unaffected
- Edge case covered (oversized chunk raises a clear MemoryError)
- Dask chunk path handled (guard runs inside the task, fires at compute)
- No premature materialization or extra copies
- Benchmark not needed (no perf-path change)
- README/docs not applicable (no public API change)
- Pure bug fix, no docstring changes needed
…ost_distance-2026-06-15-01
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 #3343
What
_check_memory(h, w)inside the_make_chunk_funcclosure before running_cost_distance_kernel, so the numpyda.map_overlappath raises a cleanMemoryError(pointing atmax_cost=or smaller chunks) instead of an opaque allocator failure on an oversized chunk.This was the only allocation path in the module without a guard. The numpy (
_cost_distance_numpy), cupy/dask+cupy (_check_gpu_memory), and iterative tile Dijkstra paths already had one.Backend coverage
_check_gpu_memory).Test plan
test_dask_map_overlap_chunk_memory_guard_raisesforces the map_overlap path with a finitemax_costand a chunk size larger than the overlap pad, mocks_available_memory_byteslow, and asserts aMemoryErrormentioningmax_costat compute time.main(DID NOT RAISE) and passes with the fix.test_cost_distance.pysuite passes (56 passed), covering numpy, dask+numpy, cupy, and dask+cupy.