Spoc_core.Transferval alloc_scalar_buffer :
Device.t ->
int ->
('a, 'b) Vector.scalar_kind ->
Vector.device_bufferAllocate a device buffer for a scalar vector, returning a DEVICE_BUFFER module. The buffer is packaged with its backend for type-safe operations.
For CPU devices (OpenCL CPU, Native), uses zero-copy allocation when possible to avoid memory transfers entirely.
val alloc_scalar_buffer_zero_copy :
Device.t ->
('a, 'b, Stdlib.Bigarray.c_layout) Stdlib.Bigarray.Array1.t ->
('a, 'b) Vector.scalar_kind ->
Vector.device_buffer optionAllocate a device buffer using zero-copy (host memory sharing) if supported. Returns None if the backend doesn't support zero-copy for this device.
val alloc_custom_buffer : Device.t -> int -> int -> Vector.device_bufferAllocate a device buffer for a custom vector
val ensure_buffer : ('a, 'b) Vector.t -> Device.t -> Vector.device_bufferEnsure vector has a device buffer, allocating if needed. For backends that support zero-copy (typically CPU backends), automatically uses zero-copy to avoid memory transfer overhead. The backend decides via alloc_zero_copy.
module Read_back : sig ... endEverything that reads device memory back into host storage, and NOTHING else. The module exists for its signature: copy_device_to_host is defined inside and deliberately absent from it, so the only code that can call the packed-buffer read directly is Read_back.read_back_to_host below.
val to_cpu : ?force:bool -> ('a, 'b) Vector.t -> unitTransfer vector data from device to CPU.
val sync : ('a, 'b) Vector.t -> unitEnsure vector is fully synchronized
Release the device memory vec holds on dev, draining it to host storage first.
FOUR steps, and not one of them may sit inside get_buffer's Some arm. Under the SoA ABI the leaves are the only device memory the vector has and the packed buffer is never allocated, so on a transparent vector get_buffer returns None ALWAYS — an early return there does not skip a corner case, it skips the whole function.
The previous version of this comment already said "under the SoA ABI they are the only device memory this vector has" while the bookkeeping below it still lived in the Some buf arm and assumed a packed buffer existed. That mismatch was the bug, not a wording slip: the location reset is the only code here that assigns CPU, so a transparent SoA vector came out of this function with its leaves freed and location still naming dev. Two measured consequences, both on a vector whose data was intact in host storage:
to_cpu ~force:true raised Failure "to_cpu: no device buffer to transfer from" — Both dev plus force means "read the device back", and there is nothing there to read;to_device on the same device took the "skip (Both)" short-circuit and allocated nothing, reinstating the exact short-circuit the Stale_CPU work earlier in this branch exists to eliminate.free_all_buffers was never exposed to this because it assigns CPU unconditionally at the end. This function now does the same, scoped to dev.
val free_all_buffers : ('a, 'b) Vector.t -> unitFree all device buffers for a vector
val flush : Device.t -> unitSynchronize all pending operations on a device
module type STREAM = sig ... endStream handle - packages backend stream with its operations
type stream = (module STREAM)val synchronize_stream : stream -> unitval destroy_stream : stream -> unitval to_cpu_all : ('a, 'b) Vector.t list -> unitval sync_all : ('a, 'b) Vector.t list -> unit