Module Tolk.Realize

Linear execution and kernel dispatch.

A runner (Runner.t) is the common dispatch interface for executable operations: compiled kernels and buffer copies. Compiled_runner compiles kernel programs and creates runners. run_linear executes a Tolk_uop.Ops.t.Linear node, resolving each call's buffer arguments through a Buffers.t binding.

See also Runtime program handle for the low-level device dispatch handle.

Runners

module Runner : sig ... end

Common dispatch interface.

Local size optimization

val optimize_local_size : device:Device.t -> Device.prog -> int array -> Device.Buffer.t list -> int array

optimize_local_size ~device prg global_size rawbufs finds the local workgroup size that minimises execution time for prg with global_size.

Enumerates all valid local sizes (each dimension drawn from powers of two up to 1024, total product at most 1024), tries each twice in random order, and returns the fastest.

When the first buffer in rawbufs also appears later in the list, a temporary buffer is allocated to avoid clobbering output during measurement.

Raises Invalid_argument if every candidate fails.

Compiled runner

module Compiled_runner : sig ... end

Kernel compilation and dispatch.

Buffer copy

val buffer_copy : device:Device.t -> total_sz:int -> dest_device:string -> src_device:string -> Runner.t

buffer_copy ~device ~total_sz ~dest_device ~src_device is a runner that copies data between buffers. It uses the destination allocator's native transfer hook when Device.Buffer.supports_transfer holds, otherwise it falls back to a host-memory bounce. dest_device and src_device are device names used in the display string.

Raises Invalid_argument if the two buffers differ in size or dtype, or if the argument list does not contain exactly two buffers.

Kernel compilation

val pm_compile : device:Device.t -> to_program:(Tolk_uop.Uop.t -> Tolk_uop.Uop.t) -> Tolk_uop.Uop.t -> Tolk_uop.Uop.t

pm_compile ~device ~to_program linear rewrites every kernel Tolk_uop.Ops.t.Call in linear whose body is a Tolk_uop.Ops.t.Sink into a call whose body is the compiled Tolk_uop.Ops.t.Program returned by to_program. Tolk_uop.Ops.t.Slice and Tolk_uop.Ops.t.Copy calls are left unchanged.

Compiled programs are cached by the kernel's semantic key and the device, so kernels that differ only by diagnostic tags share one compilation.

Capture registry

val capturing : (Tolk_uop.Uop.t -> (string * int) list -> unit) list Stdlib.ref

capturing is the schedule-capture registry. While non-empty, Schedule.create_linear_with_vars hands each linearized schedule and its variable bindings to the head entry and returns an empty Tolk_uop.Ops.t.Linear instead of planning the schedule for execution. Jit.call installs its capturer here for the duration of the capture run.

Buffer binding

type buffer =
  1. | Single of Device.Buffer.t
    (*

    A buffer on one device.

    *)
  2. | Multi of Device.Multi_buffer.t
    (*

    One buffer per device of a multi-device placement.

    *)

The type for concrete buffers named by call arguments.

module Buffers : sig ... end

Maps buffer UOps to concrete device buffers.

type exec_context = {
  1. var_vals : (string * int) list;
  2. input_uops : Tolk_uop.Uop.t array;
  3. jit : bool;
  4. wait : bool;
}

Execution context threaded through a LINEAR run: symbolic variable values, the input buffer nodes that Tolk_uop.Ops.t.Param slots index into, and the JIT and wait flags.

val exec_context : ?var_vals:(string * int) list -> ?input_uops:Tolk_uop.Uop.t array -> ?jit:bool -> ?wait:bool -> unit -> exec_context

exec_context ?var_vals ?input_uops ?jit ?wait () builds a context. All fields default to empty or false.

val resolve_buffer : Buffers.t -> exec_context -> Tolk_uop.Uop.t -> buffer

resolve_buffer binding ctx node is the concrete buffer named by call argument node: a Tolk_uop.Ops.t.Param resolves through ctx.input_uops; a Tolk_uop.Ops.t.Slice is an offset view of its resolved source (per underlying device when the source is multi-device); a Tolk_uop.Ops.t.Buffer is resolved through binding; a Tolk_uop.Ops.t.Mselect indexes one shard of its multi-device source; a Tolk_uop.Ops.t.Mstack joins its per-device sources into a multi-device buffer.

Raises Invalid_argument on an unbound parameter, a symbolic slice offset, or a node that does not name a buffer.

resolve binding ctx node is resolve_buffer for a node that names a single-device buffer.

Raises Invalid_argument if node names a multi-device buffer, and in the resolve_buffer failure cases.

Linear execution

val run_linear : device:Device.t -> to_program:(Tolk_uop.Uop.t -> Tolk_uop.Uop.t) -> Buffers.t -> ?var_vals:(string * int) list -> ?input_uops:Tolk_uop.Uop.t array -> ?jit:bool -> ?wait:bool -> Tolk_uop.Uop.t -> unit

run_linear ~device ~to_program binding ?var_vals ?input_uops ?jit ?wait linear executes each Tolk_uop.Ops.t.Call in the Tolk_uop.Ops.t.Linear linear in order.

When jit is false (default), linear is first compiled with pm_compile, turning each kernel Tolk_uop.Ops.t.Sink body into a Tolk_uop.Ops.t.Program; when jit is true, linear is assumed already compiled. Each call is then dispatched on its body: a Tolk_uop.Ops.t.Program is launched with launch dimensions and scalar arguments read from its Tolk_uop.Uop.program_info and a device handle built from its compiled binary; a Tolk_uop.Ops.t.Slice binds a view of its resolved source into binding; a Tolk_uop.Ops.t.Copy transfers between its resolved buffers; a Tolk_uop.Ops.t.Custom_function named "graph" records its LINEAR body into the device's Device.Graph on first execution and replays that graph afterwards, patching per run every buffer argument whose resolution changed (arguments reaching a Tolk_uop.Ops.t.Param slot or a binding reseeded through Buffers.seed), variable values, and symbolic launch dimensions. Buffer arguments are resolved with resolve_buffer, so Tolk_uop.Ops.t.Param slots index into input_uops.

A call whose arguments resolve to multi-device buffers executes once per device position: a kernel launches its one compiled program on each device with the device index bound to the _device_num variable, and a copy transfers each per-device pair (natively when the devices share a backend, through a host bounce otherwise).

wait is forced to true when DEBUG >= 2.

val graph_launches : int Stdlib.ref

graph_launches counts batched graph launches dispatched through Device.Graph execs, including each graph's recording launch. A cumulative observability counter for tests and debugging.