Skip to main content

Module dispatch

Module dispatch 

Source
Expand description

Capability-driven dispatch tables. Phase 7.5 A5.

CapabilityRegistry collects BackendCapabilities from each registered backend; TransferMatrix encodes the cheapest path between every pair of registered devices.

Together they let DAG construction (Phase B) answer two questions:

  1. Which backend should handle (op, dtype)? — query CapabilityRegistry::find_backends / crate::dispatch::CapabilityRegistry::find_backend_for to get the set of registered backends that support the pair.
  2. How does data move between two devices? — query TransferMatrix::path for the chosen path; falls back to HostStaging if no direct path exists.

The registry is process-wide (typically initialized once at application startup via OnceLock) but exposed here as a value so tests and alternative dispatch policies can construct their own. The canonical process-wide instance lives below (global_registry / global_bindings).

Structs§

CapabilityRegistry
Collection of backend capabilities, queried during DAG construction to pick which backend handles each op.
TransferMatrix
Lookup table mapping (source_device, dest_device) pairs to the cheapest available TransferPath. Built once at registration time from each backend’s advertised outbound paths; consulted every time the DAG inserts an Op::Move / Op::Copy.

Functions§

bump_topology_generation
Bump the topology generation counter. Every component that can change the visible set of backends or devices should call this after the mutation lands so consumers see a self-healing SystemTopology::current() next call. Cheap (one atomic add); Release ordering pairs with the Acquire load in topology_generation.
cpu_input
Helper: extract &CpuStorageBytes from &Storage. Returns Err if the variant isn’t BackendStorage::Cpu.
cpu_output
Helper: extract &mut CpuStorageBytes from &mut Storage. pub (along with cpu_input, read_storage, write_storage) so external backend crates (fuel-mkl-cpu-backend, fuel-aocl-cpu-backend) can build their own binding-table wrappers without reimplementing the lock + dtype-match shape.
derive_backend_caps
FKC cost unification — Part A.
extend_global_bindings
Add a backend’s dispatch wrappers to the process-wide binding table. Each backend exposes a register_*_kernels(table) function (see register_cpu_kernels); per-backend init paths call this to plug their wrappers into the global table.
global_bindings
Take a snapshot of the process-wide kernel-binding table. CPU dispatch wrappers are auto-registered on first access. When built with the cuda feature, the CUDA PTX path + the baracuda-kernels-sys path are also auto-registered — production callers picking up the global table see all available backends without manual init.
global_registry
inflight_count
Read the current in-flight async-op count for loc. The signal a load-aware selector consults (Step E Phase C / C2, via BackendStreams::pending_work_count). 0 for a device that has never submitted async work (including CPU, which never submits any).
inflight_dec
Decrement the in-flight async-op count for loc — called when an A4b completion handle RETIRES (its Drop). Underflow-saturating: if the slot is missing or already 0 this is a no-op, so an unexpected extra dec can never wrap the count to u32::MAX and poison future scheduling. (Under the balance invariant — every inc is matched by exactly one dec via the handle’s Drop — this saturation never actually fires; it is pure defense.)
inflight_inc
Increment the in-flight async-op count for loc — called when the executor SUBMITS an async op (the same site that builds an A4b completion handle). Cheap: a single relaxed fetch_add once the per-device slot exists (the slot is created once, under a brief write lock, on the first submit to a device). A poisoned lock is swallowed (the counter is a hint; a poisoned table just stops tracking — never a panic on a production path).
read_storage
Acquire a poisoned-lock-aware read guard. Lock poisoning is a programming bug (a previous writer panicked while holding the lock); production code surfaces it as a typed error rather than re-panicking through unwrap.
register_backend_capabilities
Add a backend’s capabilities to the process-wide registry. Typically called by each backend’s init / probe path during app startup. Idempotent for the same (backend_id, device_location) is the caller’s responsibility — the registry happily appends duplicates and the lookup picks the first-registered match.
register_cpu_kernels
Register CPU dispatch wrappers in the binding table. Call once at process startup or on first table creation. The CPU backend is the universal fallback; its bindings cover every standard (op, dtype) combination after Phase C migration. Today’s set covers the elementwise binary + unary f32 families.
register_default_fused_kernels
Phase 7.6 step 6 — register the always-built fused-op kernels into the crate::fused::FusedKernelRegistry. Called by crate::fused::default_kernel_registry; kept here so the crate-private CPU dispatch wrappers stay co-located with their registration.
resolve_target_backend
Resolve which backend should handle (op, dtype) given the registry of available backends. Returns the chosen BackendId or Error::NoBackendForOp with diagnostic data on miss.
resolve_target_backend_residency_aware
Residency-aware variant of resolve_target_backend. Prefers backends that have at least one input already resident on their device, breaking ties only on the residency axis (within the “supports (op, dtype)” set). Falls back to registration order when no candidate has local input residency.
topology_generation
Read the current topology generation. Used by SystemTopology’s current() to decide whether its cached snapshot is still valid.
write_storage