Module Spoc_core_base.Make

Parameters

module Ops : CUSTOM_OPS

Signature

Element types

type ('a, 'b) scalar_kind =
  1. | Float16 : (float, Stdlib.Bigarray.float16_elt) scalar_kind
  2. | Float32 : (float, Stdlib.Bigarray.float32_elt) scalar_kind
  3. | Float64 : (float, Stdlib.Bigarray.float64_elt) scalar_kind
  4. | Int32 : (int32, Stdlib.Bigarray.int32_elt) scalar_kind
  5. | Int64 : (int64, Stdlib.Bigarray.int64_elt) scalar_kind
  6. | Char : (char, Stdlib.Bigarray.int8_unsigned_elt) scalar_kind
  7. | Complex32 : (Stdlib.Complex.t, Stdlib.Bigarray.complex32_elt) scalar_kind
type location =
  1. | CPU
  2. | GPU of Ops.device_t
  3. | Both of Ops.device_t
  4. | Stale_CPU of Ops.device_t
  5. | Stale_GPU of Ops.device_t
type 'a custom_type = {
  1. elem_size : int;
  2. type_id : 'a Sarek_ir_types.Type_id.t;
  3. vector_type_id : ('a, unit) t Sarek_ir_types.Type_id.t;
  4. get : Ops.handle -> int -> 'a;
  5. set : Ops.handle -> int -> 'a -> unit;
  6. name : string;
  7. ir_fields : (string * Sarek_ir_types.elttype) list option;
    (*

    Immediate fields of the element type in declaration order, when the element is a flat scalar record whose byte layout is derivable by Sarek_ir_layout.record_layout; None when it is not (variants, hand-written descriptors, unsupported field types). None means "no SoA plan derivable", never "no fields".

    Carries the same trust as elem_size: it is untyped metadata about 'a that the type system does not relate to get/set. It is sound only because each producer derives the field list, the size and the accessors from a single layout computation.

    *)
}
and (_, _) kind =
  1. | Scalar : ('a, 'b) scalar_kind -> ('a, 'b) kind
  2. | Custom : 'a custom_type -> ('a, unit) kind
and (_, _) host_storage =
  1. | Bigarray_storage : ('a, 'b, Stdlib.Bigarray.c_layout) Stdlib.Bigarray.Array1.t -> ('a, 'b) host_storage
  2. | Custom_storage : {
    1. ptr : Ops.handle;
    2. custom : 'a custom_type;
    3. length : int;
    } -> ('a, unit) host_storage
and soa_binding = {
  1. soa_num_leaves : int;
  2. soa_aos_stride : int;
  3. soa_scatter : unit -> unit;
  4. soa_gather : unit -> unit;
  5. soa_to_device : Ops.device_t -> unit;
  6. soa_leaf_bufs : Ops.device_t -> Ops.device_buf list;
  7. soa_from_device : Ops.device_t -> unit;
    (*

    Inverse of soa_to_device: read every leaf back, then gather into the packed AoS host buffer. Without it a kernel's output is silently lost — the packed device buffer an ordinary Transfer.to_cpu reads is the one the SoA launch did not write.

    *)
  8. soa_free_leaves : Ops.device_t option -> unit;
    (*

    Release the leaf device buffers — Some dev on that device, None on every device — then NARROW soa_leaves_live: still set iff it already was and some leaf survives. Without the release, Transfer.free_all_buffers returned ZERO bytes on a transparent SoA vector (the packed buffer it iterates is never allocated under this ABI): a leak, not wrong data, since each leaf also carries a Gpu_memory.register_finalizer. Without consulting the surviving leaves — it used to assign false — a per-device free disowned live leaves on every OTHER device, and that IS wrong data: the drain-before-free consults this flag. Without the conjunction with the PREVIOUS value it was wrong in the mirror direction: a packed launch clears the flag and leaves those buffers allocated, so deriving from allocation alone resurrected ownership of leaves that had already been gathered and given up.

    *)
  9. soa_leaves_live : bool Stdlib.ref;
    (*

    Does the device hold results in the LEAVES rather than in the packed AoS buffer? What lets the read-back path follow the launch's ABI decision instead of re-deriving it.

    Four writers, and only four, of which exactly one can SET it: soa_to_device sets it; Execute.transfer_vectors_to_device clears it when a launch takes the packed ABI; Transfer.to_device clears it after a packed host->device upload; soa_free_leaves narrows it. So it states the ABI of the most recent operation that made a device copy authoritative, rather than of some operation. Every read-back path only READS it. false whenever the launch used the packed AoS ABI — an external source through run_source, or any non-CUDA/PTX backend.

    *)
}

Opt-in Structure-of-Arrays binding for a custom (flat-record) vector (backlog-54); present iff the vector came from Soa_vector.create_transparent, its only producer. There is no Vector.create ~layout:SoA and no layout parameter — the Tier 1b handoff proposed one and it was rejected as a layer inversion.

Deliberately closures and plain ints rather than a Soa.plan. This library is FFI-free and also builds as .bc.js (sarek/core/ffi_free_gate enforces both), while Soa/Soa_vector use Ctypes and live above it — so the transpose crosses down as behaviour, not as types. See the implementation for the full rationale, including why this is a record field instead of the host_storage constructor the Tier 1b handoff proposed.

and ('a, 'b) t = {
  1. host : ('a, 'b) host_storage;
  2. device_buffers : (int, Ops.device_buf) Stdlib.Hashtbl.t;
  3. length : int;
  4. kind : ('a, 'b) kind;
  5. mutable location : location;
  6. mutable auto_sync : bool;
  7. id : int;
  8. mutable soa : soa_binding option;
    (*

    None for every vector but one from Soa_vector.create_transparent (no ~layout parameter exists — see soa_binding). Read only by the launch path; every host-side operation ignores it, which is why get/set and the PPX accessors are unchanged.

    *)
}

Kind helpers — pure

val to_bigarray_kind : ('a, 'b) scalar_kind -> ('a, 'b) Stdlib.Bigarray.kind
val bigarray_elem_size : ('a, 'b) Stdlib.Bigarray.kind -> int
val scalar_elem_size : ('a, 'b) scalar_kind -> int
val elem_size : ('a, 'b) kind -> int
val scalar_kind_name : ('a, 'b) scalar_kind -> string
val kind_name : ('a, 'b) kind -> string

Type-id helpers

val float16_type_id : float Sarek_ir_types.Type_id.t
val float32_type_id : float Sarek_ir_types.Type_id.t
val float64_type_id : float Sarek_ir_types.Type_id.t
val int32_type_id : int32 Sarek_ir_types.Type_id.t
val int64_type_id : int64 Sarek_ir_types.Type_id.t
val char_type_id : char Sarek_ir_types.Type_id.t
val complex32_type_id : Stdlib.Complex.t Sarek_ir_types.Type_id.t
val scalar_type_id : ('a, 'b) scalar_kind -> 'a Sarek_ir_types.Type_id.t
val type_id : ('a, 'b) kind -> 'a Sarek_ir_types.Type_id.t
val float16_vector_type_id : (float, Stdlib.Bigarray.float16_elt) t Sarek_ir_types.Type_id.t
val float32_vector_type_id : (float, Stdlib.Bigarray.float32_elt) t Sarek_ir_types.Type_id.t
val float64_vector_type_id : (float, Stdlib.Bigarray.float64_elt) t Sarek_ir_types.Type_id.t
val int32_vector_type_id : (int32, Stdlib.Bigarray.int32_elt) t Sarek_ir_types.Type_id.t
val int64_vector_type_id : (int64, Stdlib.Bigarray.int64_elt) t Sarek_ir_types.Type_id.t
val char_vector_type_id : (char, Stdlib.Bigarray.int8_unsigned_elt) t Sarek_ir_types.Type_id.t
val complex32_vector_type_id : (Stdlib.Complex.t, Stdlib.Bigarray.complex32_elt) t Sarek_ir_types.Type_id.t
val vector_type_id : ('a, 'b) kind -> ('a, 'b) t Sarek_ir_types.Type_id.t

Creation

val create_scalar : ('a, 'b) scalar_kind -> ?dev:Ops.device_t -> int -> ('a, 'b) t
val create : ('a, 'b) kind -> ?dev:Ops.device_t -> int -> ('a, 'b) t
val create_custom : 'a custom_type -> ?dev:Ops.device_t -> int -> ('a, unit) t
val of_bigarray : ('a, 'b) scalar_kind -> ('a, 'b, Stdlib.Bigarray.c_layout) Stdlib.Bigarray.Array1.t -> ('a, 'b) t
val of_raw_handle : 'a custom_type -> nativeint -> int -> ('a, unit) t

Accessors

val to_bigarray : ('a, 'b) t -> ('a, 'b, Stdlib.Bigarray.c_layout) Stdlib.Bigarray.Array1.t
val has_buffer : ('a, 'b) t -> Ops.device_t -> bool
val get_buffer : ('a, 'b) t -> Ops.device_t -> Ops.device_buf option

Custom-type marshal helpers

val custom_to_bytes : 'a custom_type -> 'a -> bytes

Serialize a custom-type value to bytes. Delegates to Ops.custom_to_bytes. Raises on jsoo builds (custom-type device path not yet implemented).

val custom_of_bytes : 'a custom_type -> bytes -> 'a

Deserialize bytes to a custom-type value. Delegates to Ops.custom_of_bytes. Raises on jsoo builds (custom-type device path not yet implemented).

Subvector metadata

type sub_meta = {
  1. parent_id : int;
  2. start : int;
  3. ok_range : int;
  4. ko_range : int;
  5. depth : int;
}
val is_sub : ('a, 'b) t -> bool
val get_sub_meta : ('a, 'b) t -> sub_meta option
val depth : ('a, 'b) t -> int
val parent_id : ('a, 'b) t -> int option
val sub_start : ('a, 'b) t -> int option
val sub_ok_range : ('a, 'b) t -> int option
val sub_ko_range : ('a, 'b) t -> int option

Copy & slicing

val copy_host_only : ('a, 'b) t -> ('a, 'b) t
val sub_vector_host : ('a, 'b) t -> start:int -> len:int -> ('a, 'b) t
val sub_vector : ('a, 'b) t -> start:int -> len:int -> ok_range:int -> ko_range:int -> ('a, 'b) t
val partition_host : ('a, 'b) t -> Ops.device_t array -> ('a, 'b) t array

List / array creation

val of_list : ('a, 'b) kind -> 'a list -> ('a, 'b) t
val of_array : ('a, 'b) kind -> 'a array -> ('a, 'b) t

Auto-sync callback

type sync_callback = {
  1. sync : 'a 'b. ('a, 'b) t -> bool;
}
val register_sync_callback : sync_callback -> unit
val ensure_cpu_sync : ('a, 'b) t -> unit

Handle access for the transfer layer

val host_handle : ('a, 'b) t -> Ops.handle
val host_raw : ('a, 'b) t -> nativeint

Convenience scalar-kind values

val float32 : (float, Stdlib.Bigarray.float32_elt) kind
val float64 : (float, Stdlib.Bigarray.float64_elt) kind
val int32 : (int32, Stdlib.Bigarray.int32_elt) kind
val int64 : (int64, Stdlib.Bigarray.int64_elt) kind
val char : (char, Stdlib.Bigarray.int8_unsigned_elt) kind
val complex32 : (Stdlib.Complex.t, Stdlib.Bigarray.complex32_elt) kind