Skip to main content

SystemTopology

Struct SystemTopology 

Source
pub struct SystemTopology { /* private fields */ }
Expand description

Single-source-of-truth view of which backends are loaded in this process, which devices exist, which backends share storage substrate (so cross-backend on the same device is free), and what transfer paths connect devices.

Construct via SystemTopology::current — the result is an Arc so a long-running consumer (a picker walking a large graph) has a stable view for the duration of its call even if a backend registers mid-walk.

Implementations§

Source§

impl SystemTopology

Source

pub fn current() -> Arc<SystemTopology> ⓘ

Return a snapshot of the current topology. Cheap when nothing has changed since the last call (one atomic load + Arc::clone); rebuilds + atomically swaps when the generation counter has advanced.

The returned Arc lives independent of the cache lock — a long-running consumer can hold the snapshot across calls that might trigger a rebuild without risk of deadlock or torn reads.

See docs/session-prompts/system-topology-service.md for the full TDP-5/TDP-6 lifecycle contract.

Source

pub fn refresh()

Force a rebuild on the next current() call, even if no registration site has bumped the generation counter. Use in tests / advanced setups where a registration path bypassed the standard helpers; production code should let registration sites bump the counter via the register_backend_capabilities / extend_global_bindings helpers in fuel-storage.

Source

pub fn generation(&self) -> u64

Generation counter this snapshot was built against. Two snapshots with equal generations report identical predicates.

Source

pub fn devices(&self) -> &[DeviceLocation]

Every distinct DeviceLocation visible on this host. Sorted for deterministic iteration; CPU is always present.

Source

pub fn backends(&self) -> &[BackendId]

Every BackendId with at least one registered kernel or capability entry. Sorted ascending by as_str().

Source

pub fn backends_for(&self, dev: DeviceLocation) -> &[BackendId]

Which backends can target this device? CPU returns [Cpu, Reference, Aocl, Mkl] (subset of compiled-in backends); Cuda { gpu_id: N } returns [Cuda]; Vulkan { gpu_id: N } returns [Vulkan]. Empty slice for an unknown device.

Source

pub fn devices_for(&self, backend: BackendId) -> &[DeviceLocation]

Which devices can this backend target? CUDA returns every Cuda { gpu_id } visible on the host. Empty slice for an unknown backend or one whose probe returned zero devices.

Source

pub fn substrate_class( &self, backend: BackendId, _device: DeviceLocation, ) -> Option<SubstrateClass>

The substrate class for a (backend, device) pair. Returns None if the backend is unknown.

Prefers the substrate declared in the backend’s BackendCapabilities when one is registered, otherwise falls back to default_substrate_for. The device argument is accepted for API symmetry with Self::shares_storage (today’s substrate classification doesn’t vary per-device but future NUMA-split CPU might).

Source

pub fn shares_storage( &self, a: (BackendId, DeviceLocation), b: (BackendId, DeviceLocation), ) -> bool

Critical predicate — do these two backends operate on the same storage substrate when both target their given device?

shares_storage((Cpu, Cpu), (Aocl, Cpu)) is true — both declare SubstrateClass::HostBytes and run on DeviceLocation::Cpu, so a kernel from either backend can consume the other’s output with no copy.

shares_storage((Cuda, Cuda{0}), (Cuda, Cuda{1})) is false — same substrate class, different devices: a peer or host-staging copy is required (see Self::transfer_path).

shares_storage((Cuda, Cuda{0}), (Vulkan, Vulkan{0})) is false — same physical silicon, but the substrates are distinct (CUDA’s allocator vs Vulkan’s VkBuffer). External- memory import is out of scope today; treat as host-staging.

Returns false if either backend is unknown.

Source

pub fn transfer_path( &self, src: DeviceLocation, dst: DeviceLocation, ) -> TransferPath

What’s needed to move bytes from src to dst? Returns the path advertised by the source backend’s capabilities, or TransferPath::SameDevice when src == dst, or TransferPath::HostStaging as the universal fallback when no specific path was advertised (every backend supports host staging by contract).

Source

pub fn transfer_calibration(&self) -> &TransferCalibration

Numeric per-path transfer calibration — Stage 1 of the load-time incremental planner (docs/session-prompts/load-time-incremental-planner.md).

The first call on a snapshot runs the calibration probe (timed H2D/D2H round-trips through the byte-storage substrate APIs, a few tens of ms per GPU device); every later call — from any thread — returns the cached result. Because the snapshot itself is rebuilt at most once per topology generation, the probe runs at most once per generation. CPU-only hosts probe zero paths and return an empty calibration immediately — no probe, no error.

Most consumers want Self::transfer_estimate / Self::estimate_transfer_ns, which add the fallback pricing for unprobed paths; this accessor exposes the raw probed set for diagnostics and tests.

Source

pub fn transfer_estimate( &self, src: DeviceLocation, dst: DeviceLocation, ) -> TransferEstimate

Numeric cost model for moving bytes src → dst, for the planner’s cost composer (Stage 2) and placement DP (Stage 3). Never fails:

  1. a probed estimate for the exact path wins;
  2. an unprobed device-to-device pair whose two host legs were both probed prices as their serial composition (today’s cross-GPU transfers route through host staging — see Self::transfer_path);
  3. anything else uses the conservative per-path-class static defaults documented on TransferEstimate::fallback_for.

Triggers the lazy calibration probe on first use (see Self::transfer_calibration).

Source

pub fn estimate_transfer_ns( &self, src: DeviceLocation, dst: DeviceLocation, bytes: u64, ) -> u64

Estimated wall-clock nanoseconds to move bytes from src to dst. Zero when src == dst (no transfer needed). Saturating, monotonic in bytes, and never panics — unprobed paths use the conservative fallbacks, never a garbage zero.

Source

pub fn capabilities(&self, backend: BackendId) -> Option<&BackendCapabilities>

Per-backend BackendCapabilities snapshot if the backend has registered with crate::dispatch::register_backend_capabilities. Backends that only registered kernels into the binding table (most production paths today) return None. The picker should not assume capabilities are present; it can fall back to the binding-table walk via Self::binding_op_coverage.

Source

pub fn binding_op_coverage( &self, backend: BackendId, ) -> &HashSet<(OpKind, DType)>

(op, dtype) pairs derived from the live binding table for a backend — the source of truth for “what kernels exist.” Empty set if the backend has no registered kernels. Note that the binding table key includes a per-operand dtype list; this flattens it to the output dtype (the last in the list) for the classic (op, dtype) shape consumers expect.

Source

pub fn capabilities_op_coverage_divergence( &self, ) -> Vec<(BackendId, OpKind, DType)>

TDP-7 divergence guard: for every backend that advertises an op_dtype_support set in BackendCapabilities, assert every advertised pair has a corresponding entry in the live binding table. Returns the list of (backend, op, dtype) pairs that were advertised but not registered. Empty list = no divergence. Used by the topology divergence test.

Trait Implementations§

Source§

impl Debug for SystemTopology

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl TransferEstimator for SystemTopology

Planner Stage-2 adapter: SystemTopology IS the production crate::ranker::TransferEstimator. compile_plan consumes transfer pricing through the trait — threaded via PlanOptions::with_transfer_estimator in pipelined_bridge::build_optimized_graph — rather than naming SystemTopology directly, so the ranker stays decoupled from the concrete topology source; the numbers come from the Stage-1 TransferCalibration behind SystemTopology::estimate_transfer_ns (probed lazily once per generation; conservative per-path-class fallbacks otherwise). Same-device queries short-circuit to zero WITHOUT touching the calibration, so CPU-only plans never trigger a probe.

Source§

fn estimate_transfer_ns( &self, src: DeviceLocation, dst: DeviceLocation, bytes: u64, ) -> u64

Estimated wall-clock nanoseconds to move bytes from src to dst. Zero when src == dst.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more