Module Spoc_core.Soa_vector

type packed_leaf =
  1. | Leaf : ('e, 'f) Vector.t -> packed_leaf

A per-leaf host buffer, type-erased: a width-matched scalar vector used as a bit-preserving byte transport for one SoA leaf.

type 'a t

A SoA custom vector of element type 'a.

val create : 'a Vector.custom_type -> int -> 'a t

create custom length builds a SoA custom vector of length elements. custom is the PPX-generated custom type (the AoS source of truth), and the leaf layout is derived from it via custom.ir_fields.

There is deliberately no ~fields parameter. There used to be one, on the premise that the custom_type carried no layout; ir_fields now does, and the PPX fills it for every [@@sarek.type] record from the same source as elem_size/get/set. A caller-supplied list was the only way for the described layout to disagree with the real one — and that disagreement was not an error but silently transposed data, since scatter/gather would index at the wrong byte offsets. Deriving makes that unreachable instead of checking for it.

Raises Soa.Unsupported if the element type has no derivable flat-record layout (ir_fields = None, i.e. a variant) or is otherwise not SoA-representable.

val aos_vector : 'a t -> ('a, unit) Vector.t

The AoS host vector (source of truth). Use it for the non-PTX host fallback (drive it through the ordinary Sarek.Execute.run_vectors AoS path).

val plan : 'a t -> Soa.plan

The SoA storage plan (leaf enumeration + AoS stride).

val leaves : 'a t -> packed_leaf array

The N per-leaf host buffers, in Soa.plan leaf (record declaration) order.

val num_leaves : 'a t -> int

Number of scalar leaves (= number of base pointers a kernel launch binds).

val length : 'a t -> int

Number of elements.

val set : 'a t -> int -> 'a -> unit

set t i v / get t i delegate to the AoS host vector (the source of truth).

val get : 'a t -> int -> 'a
val scatter : 'a t -> unit

scatter t transposes the AoS host buffer into the N per-leaf host buffers (call before transferring the leaves to a device). When the host copy is behind a device's data (Stale_CPU), scatter first reads two auto-sync gates: this vector's own flag (Vector.t.auto_sync) and the global mode (Transfer.is_auto).

With both on, scatter proceeds and calls Vector.ensure_cpu_sync to bring the host copy up to date first. Whether that actually refreshes anything is up to the registered sync callback; scatter does not verify that it did.

With EITHER gate off (this vector's own flag via Vector.set_auto_sync, or the global mode via Transfer.disable_auto / Transfer.set_auto), scatter refuses: it raises Soa.Unsupported rather than transpose host bytes it cannot establish are current, and the message names whichever gate is off. That is a deliberate over-refusal and not a claim that no rescue was possible — a caller that installed its own callback through Vector.register_sync_callback may have a path scatter does not reason about.

CPU, Both and Stale_GPU are unaffected either way, since the host copy there is already the vector's freshest data. Pure GPU is not refused either, but for a weaker reason and not because the host copy is fresh: nothing in this library puts an AoS vector into that state, so it is an uncovered gap rather than a state this function has cleared.

val gather : 'a t -> unit

gather t transposes the N per-leaf host buffers back into the AoS host buffer (call after reading leaves back from a device that wrote them).

val create_transparent : 'a Vector.custom_type -> int -> ('a, unit) Vector.t

create_transparent custom length is a SoA custom vector presented as an ordinary Vector.t: the generic Sarek.Execute.run_vectors recognises it and binds the N-leaf ABI on CUDA/PTX, falling back to the packed AoS path on every other backend. Callers need no SoA-specific launch entry point (backlog-54).

The returned vector IS the AoS source of truth — Vector.get/set and the PPX accessors work on it exactly as on any custom vector, and the transpose to and from the per-leaf buffers happens inside the launch.

Use this rather than create + Sarek.Soa_launch.run_soa unless you need to drive the leaves yourself. create remains the lower-level entry point.

Raises Soa.Unsupported on an element type with no derivable flat-record layout, exactly as create does.