neu_proc.ops.backend¶
Which library runs an op: scipy on the CPU, or cupyx on the GPU.
Dispatch follows the array, never a flag: cupyx.scipy.ndimage needs a cupy.ndarray,
so one body serves both backends:
def dilate(array, *, radius_vox):
ndi, array = ndimage_for(array, "grey_dilation")
return ndi.grey_dilation(array, footprint=kernels.ball(radius_vox))
An op handed a CPU array may still compute on the GPU and copy back, when it has enough
arithmetic to pay for the transfers; each op declares this with stage(). Measured on
an RTX A6000, an EDT dilation of a 192^3 array takes 811 ms in scipy, 16 ms on the GPU
including both transfers, and 2 ms on an array already there. A bandwidth-bound op (a
threshold, a cast) cannot win the round trip and should not stage. like() returns
each result to where the input lived, so dilate(numpy_array) returns numpy. For a
chain of ops, move once with to_gpu() and every step stays on the GPU.
NEU_PROC_GPU=0 (or backend.enabled = False) makes to_gpu() a no-op, so
nothing moves; a GPU array handed in is still processed on the GPU. cupyx lacks a few
ndimage functions (distance_transform_bf, distance_transform_cdt,
geometric_transform, watershed_ift); ndimage_for() then falls back to the CPU
and warns once. When timing GPU code, call synchronize() first: cupy queues work and
returns immediately, and the first call in a process also pays CUDA setup.
Attributes¶
Functions¶
|
|
|
|
|
True if cupy imports and a GPU device is found; cached after the first call. cupy |
|
True if |
|
|
|
|
|
|
|
Wait for queued GPU work to finish. Needed only when timing: cupy returns before the |
|
|
|
|
Module Contents¶
- neu_proc.ops.backend.array_module(value: Any)¶
cupyfor a GPU array,numpyotherwise (the array-APIxp).
- neu_proc.ops.backend.cpu_only(value: Any, what: str)¶
valueon the CPU, warning once perwhatif it had to be moved. For libraries with no GPU implementation (fastmorph, cc3d, fill_voids, edt), which would otherwise fail with cupy’s unhelpful “Implicit conversion to a NumPy array” error.
- neu_proc.ops.backend.gpu_available() bool¶
True if cupy imports and a GPU device is found; cached after the first call. cupy can import on a machine with no GPU or driver and fail only at first use, hence the device check.
- neu_proc.ops.backend.is_gpu_array(value: Any) bool¶
True if
valueis a cupy array. Checked by module name, so cupy is never imported.
- neu_proc.ops.backend.like(result: Any, reference: Any)¶
resultmoved to whereverreferencelives, CPU or GPU, so an op returns the kind of array it was given.
- neu_proc.ops.backend.ndimage_for(value: Any, need: str | None = None)¶
(ndimage module, array)forvalue.neednames the function about to be called. Ifvalueis on the GPU and cupyx lacks that function, the array is moved to the CPU and scipy is returned, with a warning once per name. Withoutneedthe array is returned untouched.
- neu_proc.ops.backend.stage(value: Any, *, prefer_gpu: bool = True)¶
valuewhere the op should compute: moved to the GPU ifprefer_gpuand a GPU is enabled and available, otherwise unchanged. Passprefer_gpu=Falsefor a bandwidth-bound op. Pair withlike():work = stage(arr) ... return like(result, arr)
- neu_proc.ops.backend.synchronize() None¶
Wait for queued GPU work to finish. Needed only when timing: cupy returns before the work is done, while reading a result synchronizes on its own.
- neu_proc.ops.backend.to_cpu(value: Any)¶
valueas a numpy array, moved off the GPU if it is on one.
- neu_proc.ops.backend.to_gpu(value: Any)¶
valueon the GPU; unchanged, without error, ifenabledis false or no GPU is available.
- neu_proc.ops.backend.enabled: bool¶
- neu_proc.ops.backend.logger¶