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:
- Which backend should handle
(op, dtype)? — queryCapabilityRegistry::find_backends/crate::dispatch::CapabilityRegistry::find_backend_forto get the set of registered backends that support the pair. - How does data move between two devices? — query
TransferMatrix::pathfor the chosen path; falls back toHostStagingif 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§
- Capability
Registry - Collection of backend capabilities, queried during DAG construction to pick which backend handles each op.
- Transfer
Matrix - Lookup table mapping
(source_device, dest_device)pairs to the cheapest availableTransferPath. Built once at registration time from each backend’s advertised outbound paths; consulted every time the DAG inserts anOp::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);Releaseordering pairs with theAcquireload intopology_generation. - cpu_
input - Helper: extract
&CpuStorageBytesfrom&Storage. Returns Err if the variant isn’tBackendStorage::Cpu. - cpu_
output - Helper: extract
&mut CpuStorageBytesfrom&mut Storage.pub(along withcpu_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 (seeregister_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
cudafeature, 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, viaBackendStreams::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 (itsDrop). 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 tou32::MAXand poison future scheduling. (Under the balance invariant — everyincis matched by exactly onedecvia the handle’sDrop— 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 relaxedfetch_addonce 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
f32families. - register_
default_ fused_ kernels - Phase 7.6 step 6 — register the always-built fused-op kernels into
the
crate::fused::FusedKernelRegistry. Called bycrate::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 chosenBackendIdorError::NoBackendForOpwith 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