Module Advanced.Pinned

Page-locked host memory, explicit and opt-in: nothing in SPOC pins memory unless you call this module. A thin owner-handle over Transfer.alloc_pinned and Transfer.register_host, which hold the mechanics (per-backend behaviour, measured pitfalls on HIP, the in-flight copy check).

The memory is a Bigarray, not a Vector.t: a vector's host mirror is garbage collected, pinned memory is released only explicitly (sub-arrays could outlive a finaliser's decision). Use it as the host side of Transfer.copy_to_device / Transfer.copy_to_host with ~stream, or as the destination of pread/O_DIRECT reads.

Release a handle only through Pinned.release: freeing or unregistering its array directly with Transfer leaves the handle believing it is live, and a later Pinned.release could then act on a different allocation at the same address.

type ('a, 'b) t

A live-or-released pinned buffer. Abstract: its state changes only through release.

val is_available : Device.t -> bool

true iff dev's backend supports page-locked memory. Probes with a one-byte alloc and release, which costs a driver allocation and free per call on GPU backends and flushes pending deferred launches (the free does). Native, Interpreter and Metal answer true (their pinned memory is plain host memory). Only the backend's Feature_not_supported refusal means false; any other error (driver out-of-memory, unknown framework) propagates.

val alloc : Device.t -> ('a, 'b) Stdlib.Bigarray.kind -> int -> ('a, 'b) t

Allocate n elements of page-locked memory for DMA with the device. Raises Invalid_argument for n < 1 or Bigarray.float16 (use a char buffer and Transfer.copy_bytes_to_device), and the backend's Feature_not_supported where unsupported (see is_available). On HIP the allocation can be slow for multi-GiB sizes: measure, or use register.

val register : Device.t -> ('a, 'b, Stdlib.Bigarray.c_layout) Stdlib.Bigarray.Array1.t -> ('a, 'b) t

Page-lock an array you already own. The array stays yours and usable after release; the handle roots it until then. Raises Invalid_argument if it is empty or already pinned or registered, and Feature_not_supported where the backend cannot register host memory. See Transfer.register_host for the measured pitfalls (copies right after registering, registrations spanning two regions).

val device : ('a, 'b) t -> Device.t
val is_live : ('a, 'b) t -> bool
val bigarray : ('a, 'b) t -> ('a, 'b, Stdlib.Bigarray.c_layout) Stdlib.Bigarray.Array1.t

The page-locked array. Raises Invalid_argument once released: the memory of an alloc handle is gone then, so do not keep the array itself past release.

val release : ('a, 'b) t -> unit

Free the memory (alloc) or unregister it (register). Idempotent. Refused with Invalid_argument, the handle staying live and the release retryable, while stream-ordered copies may still use the memory: synchronize the stream (or an event recorded after them) first.

val with_alloc : ?stream:Transfer.stream -> Device.t -> ('a, 'b) Stdlib.Bigarray.kind -> int -> (('a, 'b) t -> 'r) -> 'r

with_alloc ?stream dev kind n f runs f on a fresh alloc, then synchronizes stream (if given) and releases the handle. If f raises, its exception is re-raised after a best-effort release. f must not let the array escape. Stream-ordered copies f enqueued must be on stream, or synchronized by f: otherwise the release is refused, the refusal (Invalid_argument) is raised when f returned normally, and the handle is unreachable, so the allocation stays pinned for the process lifetime.