Skip to content

Guard diffuse() against unbounded allocations and steps loop (#1267)#1268

Merged
brendancol merged 1 commit intomainfrom
issue-1267
Apr 26, 2026
Merged

Guard diffuse() against unbounded allocations and steps loop (#1267)#1268
brendancol merged 1 commit intomainfrom
issue-1267

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Closes #1267.

Summary

  • Adds peak-memory guards (_check_memory / _check_gpu_memory, cost_distance pattern, ~32 B/pixel) that fire before the eager numpy / cupy paths allocate per-step buffers.
  • Stops materialising np.full(agg.shape, alpha) up front for scalar diffusivity. Dask paths now keep the scalar through to the per-chunk worker; eager paths only build the full alpha raster after the memory guard runs.
  • Teaches _diffuse_dask_cupy to handle scalar alpha lazily via cp.full per chunk, matching _diffuse_dask_numpy.
  • Caps steps at _MAX_STEPS = 100_000 via _validate_scalar(max_val=...) so a single call can't pin a CPU forever.

Test plan

  • All existing diffusion tests still pass (15/15)
  • New test: oversize raster raises MemoryError (with _available_memory_bytes monkeypatched)
  • New test: dask + scalar diffusivity does not call np.full(agg.shape)
  • New test: dask scalar-alpha matches dask DataArray-alpha (uniform value)
  • New test: steps=10**12 raises ValueError
  • New test: memory guard fires before np.full runs on the oversize input

- Add _check_memory / _check_gpu_memory helpers (cost_distance pattern,
  ~32 B/pixel budget) and call them from _diffuse_numpy / _diffuse_cupy
  before per-step buffers are allocated.
- Defer the np.full alpha allocation: dask + scalar diffusivity now
  passes the scalar straight through, and the eager paths only build
  the full raster after the memory guard runs.
- Teach _diffuse_dask_cupy to handle scalar alpha lazily via cp.full
  per chunk, mirroring _diffuse_dask_numpy.
- Cap steps at _MAX_STEPS (100,000) so a single call cannot pin a CPU.
- Add tests covering oversize raster -> MemoryError, scalar diffusivity
  on dask -> no full-raster np.full, dask scalar matches dask array,
  and steps above the cap -> ValueError.
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label Apr 25, 2026
@brendancol brendancol merged commit d628ccd into main Apr 26, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

diffusion: unbounded allocations and unbounded steps loop

1 participant