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
impl SystemTopology
Sourcepub fn current() -> Arc<SystemTopology> ⓘ
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.
Sourcepub fn refresh()
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.
Sourcepub fn generation(&self) -> u64
pub fn generation(&self) -> u64
Generation counter this snapshot was built against. Two snapshots with equal generations report identical predicates.
Sourcepub fn devices(&self) -> &[DeviceLocation]
pub fn devices(&self) -> &[DeviceLocation]
Every distinct DeviceLocation visible on this host. Sorted
for deterministic iteration; CPU is always present.
Sourcepub fn backends(&self) -> &[BackendId]
pub fn backends(&self) -> &[BackendId]
Every BackendId with at least one registered kernel or
capability entry. Sorted ascending by as_str().
Sourcepub fn backends_for(&self, dev: DeviceLocation) -> &[BackendId]
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.
Sourcepub fn devices_for(&self, backend: BackendId) -> &[DeviceLocation]
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.
Sourcepub fn substrate_class(
&self,
backend: BackendId,
_device: DeviceLocation,
) -> Option<SubstrateClass>
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).
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.
Sourcepub fn transfer_path(
&self,
src: DeviceLocation,
dst: DeviceLocation,
) -> TransferPath
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).
Sourcepub fn transfer_calibration(&self) -> &TransferCalibration
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.
Sourcepub fn transfer_estimate(
&self,
src: DeviceLocation,
dst: DeviceLocation,
) -> TransferEstimate
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:
- a probed estimate for the exact path wins;
- 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); - 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).
Sourcepub fn estimate_transfer_ns(
&self,
src: DeviceLocation,
dst: DeviceLocation,
bytes: u64,
) -> u64
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.
Sourcepub fn capabilities(&self, backend: BackendId) -> Option<&BackendCapabilities>
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.
Sourcepub fn binding_op_coverage(
&self,
backend: BackendId,
) -> &HashSet<(OpKind, DType)>
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.
Sourcepub fn capabilities_op_coverage_divergence(
&self,
) -> Vec<(BackendId, OpKind, DType)>
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
impl Debug for SystemTopology
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.
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
fn estimate_transfer_ns( &self, src: DeviceLocation, dst: DeviceLocation, bytes: u64, ) -> u64
bytes from src
to dst. Zero when src == dst.Auto Trait Implementations§
impl !Freeze for SystemTopology
impl RefUnwindSafe for SystemTopology
impl Send for SystemTopology
impl Sync for SystemTopology
impl Unpin for SystemTopology
impl UnsafeUnpin for SystemTopology
impl UnwindSafe for SystemTopology
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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