# Topology

> ForkUnion for C

A machine is described along two independent axes.
A __compute domain__ is a bindable group of cores sharing a Quality-of-Service class _and_ locality — it is what a pool spawns onto, and the index a worker callback receives.
A __memory domain__ is a bank of memory with its own capacity and access cost — it is what the allocator targets, may be _cpuless_ like a CXL expander, and may serve several compute domains at once.
The axes stay separate because they don't line up: performance and efficiency cores often share one memory controller, and equally-fast cores are often split across cache clusters or sockets.

Each axis carries a __level__, a dense ordinal grouping domains of like performance.
Levels can be fewer than domains, since several domains may share one, and the two axes count in opposite directions: compute levels grow with performance, memory levels grow with _distance_, placing HBM below DDR and CXL above.
Two objects split the answers by provenance, not by axis: a `Topology` holds what the platform __declares__ — domains, cores, QoS classes, volumes, on both axes — while a `Fabric` holds what ForkUnion __observes__: per-edge latencies, bandwidths, and distances, and the per-medium tiers derived from them.
The pipeline is `harvest` all the way down: a `Topology` harvests the declared structure from the OS and stays immutable, a pool spawns on it, and a `Fabric` then harvests the observed performance from the silicon through that pool's pinned workers, pointer-chasing and streaming every reachable edge.
A memory level is a property of the medium, independent of the querying core: it keys on the best bandwidth any initiator sustains to the pool, ties split by the best latency, so a 3 TB/s HBM pool outranks DDR even at equal latency.

|                   | Compute axis            | Memory axis            |
| :---------------- | :---------------------- | :--------------------- |
| Count domains     | `compute_domains_count` | `memory_domains_count` |
| Count levels      | `compute_levels_count`  | `memory_levels_count`  |
| Level of a domain | `compute_level_in`      | `memory_level_in`      |
| Faster means      | __higher__              | __lower__              |

Names are spelled here as in Rust, which Mojo matches; C prefixes them with `fu_`, and Zig spells them in camelCase.
Rust, Zig, and Mojo all carry the domain coordinates as distinct types — `ComputeDomain`, `MemoryDomain`, and `MemoryDomainId` — while C passes plain indices.
Domain counts and compute levels answer from the `Topology`; memory levels and the per-edge magnitudes answer from a harvested `Fabric`, which C prefixes with `fu_fabric_`.

Beyond the level ordinals, two magnitudes describe a compute domain, both best-effort.
`compute_capacity_in` forwards the kernel's own rating of a core, read from `/sys/devices/system/cpu/cpuN/cpu_capacity`, normalized so the fastest core on the machine reads 1024.
Linux publishes that file on `arm64` and on Intel hybrids, and nothing like it exists on Windows or Apple, which read 0.
A kernel that rates every core alike rates nothing, so a tie also reads 0; Linux then splits the classes by their core PMUs, `cpu_core` and `cpu_atom` on Intel hybrids.
`compute_cache_bytes_per_core_in` reports each logical core's share of the deepest cache confined to a domain: one instance's bytes over the cores sharing it.
It is enumerated exactly rather than measured — from the per-core cache hierarchy in `sysfs` on Linux, CPUID leaves elsewhere on x86, `sysctl` on Apple, and the cache relationships on Windows.
Domains of identical throughput can sit behind very differently sized caches, so neither value follows from the other.
Treat both as hints, present only where the hardware volunteers them, and never as a number a program requires.

The memory axis carries its own magnitudes, all answered by the harvested `Fabric`.
`memory_distance` gives the relative cost of reaching a memory domain from a compute domain — 10 for local, higher for remote, on ACPI SLIT's scale but from measured ratios — while `memory_bandwidth` and `memory_latency` give the observed saturated read bandwidth and dependent-load latency per edge.
Back on the `Topology`, `volume_ram` and `volume_ram_in` report installed RAM, total or per domain, while `huge_pages_count_in` and `volume_huge_pages_in` report the free huge pages a domain can still back an allocation with, as a count or in bytes, and `huge_pages_count` and `volume_huge_pages` total them.
Like the compute magnitudes, these are best-effort: fabric queries read 0 before a harvest or on edges no pinned worker could reach.

Finally, `local_memory_of` bridges the axes, naming the memory domain a given compute domain should allocate from.
Where no topology is harvested, every query degrades to a single compute domain and a single memory domain rather than failing, so these loops need no conditional compilation.

`#include <forkunion/forkunion.h>`

## fu_topology_t

Opaque, cross-platform handle for the machine topology; immutable once constructed.

```c
typedef void * fu_topology_t;
```

## fu_fabric_t

Opaque handle for the measured memory fabric - latencies, bandwidths, tiers, distances.

```c
typedef void * fu_fabric_t;
```

## fu_memory_domain_id_t

An OS memory-domain id - a NUMA node - the allocators key off; -1 when there is none.

```c
typedef int fu_memory_domain_id_t;
```

## fu_topology_new

Harvests the machine topology - cores, compute and memory domains - into a handle.

```c
fu_status_t fu_topology_new(fu_topology_t *topology_out);
```

Build it once and thread it through [`fu_pool_spawn`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_pool_spawn) and the topology queries below.

- `topology_out` (out): Receives the opaque topology handle for the pool and metadata queries.

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL `topology_out`.

## fu_topology_delete

Frees a topology handle from [`fu_topology_new`](#fu_topology_new).

```c
void fu_topology_delete(fu_topology_t topology);
```

Pools never retain the topology, so it may be freed as soon as the last spawn returns.

- `topology` (in): Topology handle, may be NULL - a no-op.

## fu_logical_cores_count_in

Returns the number of logical cores in a given compute domain.

```c
fu_status_t fu_logical_cores_count_in(
    fu_topology_t topology,
    size_t compute_domain_index,
    size_t *cores_out);
```

Use this to size a per-compute-domain pool for [fu_pool_spawn_on](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_pool_spawn_on), or to weight work across compute domains of differing core counts, such as performance versus efficiency cores.

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `compute_domain_index` (in): Target compute domain, in [0, `fu_compute_domains_count()`).
- `cores_out` (out): Receives the number of cores backing that compute domain.

See also: [fu_compute_domains_count](#fu_compute_domains_count), [fu_pool_spawn_on](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_pool_spawn_on).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle, a NULL `cores_out` or an index out of range.

## fu_logical_cores_count

The number of logical cores the OS exposes - hyper-threads and every core class included.

```c
fu_status_t fu_logical_cores_count(
    fu_topology_t topology,
    size_t *cores_out);
```

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `cores_out` (out): Receives the count, fit as the thread count for [`fu_pool_spawn`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_pool_spawn).

See also: [fu_logical_cores_count_in](#fu_logical_cores_count_in) for the per-compute-domain count.

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `cores_out`.

## fu_compute_domains_count

The number of compute domains: bindable clusters of same-QoS, co-located cores.

```c
fu_status_t fu_compute_domains_count(
    fu_topology_t topology,
    size_t *count_out);
```

A compute domain is the unit a pool binds to and the index a worker callback receives. It is one axis of the topology; memory domains are the other, bridged by [`fu_local_memory_of`](#fu_local_memory_of).

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `count_out` (out): Receives 0 if unsupported, 1 on a uniform machine, 2+ per NUMA node and QoS class, e.g. Intel P/E or Arm big.LITTLE.

See also: [fu_logical_cores_count_in](#fu_logical_cores_count_in), [fu_pool_spawn_on](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_pool_spawn_on), [fu_memory_domains_count](#fu_memory_domains_count).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `count_out`.

## fu_compute_level_in

Returns the performance level of a given compute domain.

```c
fu_status_t fu_compute_level_in(
    fu_topology_t topology,
    size_t compute_domain_index,
    size_t *level_out);
```

Distinguishes performance vs efficiency cores, as in Intel P/E or ARM big.LITTLE.

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `compute_domain_index` (in): Target compute domain, in [0, `fu_compute_domains_count()`).
- `level_out` (out): Receives the level ordinal of that compute domain, where __higher__ __is__ __more__ __performant__ and 0 is the most efficient; homogeneous systems report 0 everywhere.

Notes: The compute ordinal grows with performance, while the memory tier from [fu_fabric_memory_level_in](#fu_fabric_memory_level_in) grows as media slow down - both match their native hardware conventions, so they run opposite ways by design.

See also: [fu_compute_levels_count](#fu_compute_levels_count), [fu_compute_domains_count](#fu_compute_domains_count).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle, a NULL `level_out` or an index out of range.

## fu_compute_levels_count

Returns the number of distinct compute performance levels across all compute domains.

```c
fu_status_t fu_compute_levels_count(
    fu_topology_t topology,
    size_t *count_out);
```

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `count_out` (out): Receives 0 if unsupported, 1 on homogeneous cores, 2-3 with heterogeneous cores, e.g. Intel P/E or Arm big.LITTLE.

Notes: May be smaller than `fu_compute_domains_count()` - several domains can share one level, as when equally-fast cores are split across cache clusters, or across NUMA nodes.

See also: [fu_compute_level_in](#fu_compute_level_in).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `count_out`.

## fu_compute_capacity_in

Returns the relative throughput of __one__ core in a given compute domain.

```c
fu_status_t fu_compute_capacity_in(
    fu_topology_t topology,
    size_t compute_domain_index,
    size_t *capacity_out);
```

This is the number to weight work by; [`fu_compute_level_in`](#fu_compute_level_in) is a dense ordinal and must never be divided by; on 0, weigh compute domains by [`fu_logical_cores_count_in`](#fu_logical_cores_count_in) instead.

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `compute_domain_index` (in): Target compute domain, in [0, `fu_compute_domains_count()`).
- `capacity_out` (out): Receives the relative throughput of one core in that compute domain, on the Linux `cpu_capacity` scale where 1024 is the fastest core present, or 0 when the platform publishes no per-core throughput rating.

See also: [fu_compute_level_in](#fu_compute_level_in), [fu_logical_cores_count_in](#fu_logical_cores_count_in).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle, a NULL `capacity_out` or an index out of range.

## fu_compute_cache_bytes_per_core_in

Returns each logical core's share of the deepest cache confined to a compute domain.

```c
fu_status_t fu_compute_cache_bytes_per_core_in(
    fu_topology_t topology,
    size_t compute_domain_index,
    size_t *bytes_out);
```

Sizes a cache-resident chunk - a different question from how __many__ chunks a domain deserves. Domains may sustain identical throughput yet back onto very differently sized caches, so neither number can be inferred from the other.

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `compute_domain_index` (in): Target compute domain, in [0, `fu_compute_domains_count()`).
- `bytes_out` (out): Receives the bytes of one cache instance per sharing core, 0 if unknown.

See also: [fu_compute_capacity_in](#fu_compute_capacity_in).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle, a NULL `bytes_out` or an index out of range.

## fu_memory_domains_count

Returns the number of memory domains - the distinct allocation targets.

```c
fu_status_t fu_memory_domains_count(
    fu_topology_t topology,
    size_t *count_out);
```

A __memory__ __domain__ is a bank of memory with its own capacity and access cost. It is the unit the allocator targets. A memory domain may be __cpuless__, as with a CXL expander or GPU-attached HBM, and may be local to __several__ compute domains, as when performance and efficiency cores share one DDR controller. Its performance - tiers, latencies, bandwidths, distances - is not the topology's to declare: a `[fu_fabric_t](#fu_fabric_t)` measures it in-process.

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `count_out` (out): Receives 0 if unsupported, 1 on uniform-memory systems, 2+ on NUMA / tiered-memory systems.

See also: [fu_volume_ram_in](#fu_volume_ram_in), [fu_local_memory_of](#fu_local_memory_of), [fu_allocate_on_domain_id](https://www.unum.cloud/docs/forkunion/c/numa.md#fu_allocate_on_domain_id), [fu_fabric_harvest](#fu_fabric_harvest).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `count_out`.

## fu_local_memory_of

Returns the memory domain nearest to a given compute domain.

```c
fu_status_t fu_local_memory_of(
    fu_topology_t topology,
    size_t compute_domain_index,
    size_t *memory_domain_out);
```

The convenience bridge for the common "run here, allocate near here" pattern: pass the result to [`fu_allocate_on_domain_id`](https://www.unum.cloud/docs/forkunion/c/numa.md#fu_allocate_on_domain_id). For the full cost picture use [`fu_fabric_memory_distance`](#fu_fabric_memory_distance).

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `compute_domain_index` (in): Target compute domain, in [0, `fu_compute_domains_count()`).
- `memory_domain_out` (out): Receives the index of that compute domain's nearest memory domain.

See also: [fu_fabric_memory_distance](#fu_fabric_memory_distance), [fu_allocate_on_domain_id](https://www.unum.cloud/docs/forkunion/c/numa.md#fu_allocate_on_domain_id).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle, a NULL `memory_domain_out` or a compute-domain index out of range.

## fu_volume_ram_in

Returns the RAM volume in bytes of a given memory domain.

```c
fu_status_t fu_volume_ram_in(
    fu_topology_t topology,
    size_t memory_domain_index,
    size_t *bytes_out);
```

The bytes count regardless of page size.

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `memory_domain_index` (in): Target memory domain, in [0, `fu_memory_domains_count()`).
- `bytes_out` (out): Receives the bytes of RAM in that memory domain.

See also: [fu_volume_ram](#fu_volume_ram), [fu_allocate_on_domain_id](https://www.unum.cloud/docs/forkunion/c/numa.md#fu_allocate_on_domain_id).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle, a NULL `bytes_out` or an index out of range.

## fu_volume_ram

Returns the total RAM volume in bytes across all memory domains.

```c
fu_status_t fu_volume_ram(fu_topology_t topology, size_t *bytes_out);
```

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `bytes_out` (out): Receives the number of bytes of RAM installed, regardless of page size.

See also: [fu_volume_ram_in](#fu_volume_ram_in).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `bytes_out`.

## fu_volume_huge_pages_in

Returns the huge-page volume in bytes available in a given memory domain.

```c
fu_status_t fu_volume_huge_pages_in(
    fu_topology_t topology,
    size_t memory_domain_index,
    size_t *bytes_out);
```

Huge pages reduce TLB pressure by mapping memory in larger units than the base page. The bytes are 0 if huge pages are unavailable.

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `memory_domain_index` (in): Target memory domain, in [0, `fu_memory_domains_count()`).
- `bytes_out` (out): Receives the bytes backed by free huge pages in that memory domain.

See also: [fu_huge_pages_count_in](#fu_huge_pages_count_in), [fu_allocate_at_least_on_domain_id](https://www.unum.cloud/docs/forkunion/c/numa.md#fu_allocate_at_least_on_domain_id).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle, a NULL `bytes_out` or an index out of range.

## fu_volume_huge_pages

Returns the total huge-page volume in bytes across all memory domains.

```c
fu_status_t fu_volume_huge_pages(
    fu_topology_t topology,
    size_t *bytes_out);
```

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `bytes_out` (out): Receives the bytes backed by free huge pages, 0 if unavailable.

See also: [fu_volume_huge_pages_in](#fu_volume_huge_pages_in), [fu_huge_pages_count](#fu_huge_pages_count).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `bytes_out`.

## fu_huge_pages_count_in

Returns the number of free huge pages in a given memory domain.

```c
fu_status_t fu_huge_pages_count_in(
    fu_topology_t topology,
    size_t memory_domain_index,
    size_t *pages_out);
```

The count is 0 if huge pages are unavailable.

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `memory_domain_index` (in): Target memory domain, in [0, `fu_memory_domains_count()`).
- `pages_out` (out): Receives the free huge pages, all sizes, in that memory domain.

See also: [fu_volume_huge_pages_in](#fu_volume_huge_pages_in), [fu_huge_pages_count](#fu_huge_pages_count).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle, a NULL `pages_out` or an index out of range.

## fu_huge_pages_count

Returns the total number of free huge pages across all memory domains.

```c
fu_status_t fu_huge_pages_count(
    fu_topology_t topology,
    size_t *pages_out);
```

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `pages_out` (out): Receives the count of free huge pages of any size, 0 if unavailable.

See also: [fu_volume_huge_pages](#fu_volume_huge_pages), [fu_huge_pages_count_in](#fu_huge_pages_count_in).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `pages_out`.

## fu_memory_domain_id_at_index

Resolves a memory domain's dense index to the OS id the allocators take.

```c
fu_status_t fu_memory_domain_id_at_index(
    fu_topology_t topology,
    size_t memory_domain_index,
    fu_memory_domain_id_t *memory_domain_id_out);
```

The allocators key off the OS id rather than the topology, so an allocation can outlive the handle. Look the id up once - typically near a compute domain via [`fu_local_memory_of`](#fu_local_memory_of) - then allocate and free with it alone.

- `topology` (in): Machine topology from [`fu_topology_new`](#fu_topology_new).
- `memory_domain_index` (in): Target memory domain, in [0, `fu_memory_domains_count()`).
- `memory_domain_id_out` (out): Receives the OS memory-domain id, a NUMA node.

See also: [fu_allocate_on_domain_id](https://www.unum.cloud/docs/forkunion/c/numa.md#fu_allocate_on_domain_id), [fu_local_memory_of](#fu_local_memory_of), [fu_memory_domains_count](#fu_memory_domains_count).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle, a NULL `memory_domain_id_out` or an index out of range.

## fu_pool_compute_domains_count

The number of compute domains the pool's workers span.

```c
fu_status_t fu_pool_compute_domains_count(
    fu_pool_t pool,
    size_t *count_out);
```

The count is 1 without NUMA/QoS heterogeneity, 2+ across NUMA or QoS levels.

- `pool` (in): Pool handle, must not be NULL.
- `count_out` (out): Receives the number of compute domains.

Notes: Not synchronized.

See also: [fu_pool_threads_count_in](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_pool_threads_count_in).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `count_out`.
- [`fu_not_spawned_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_not_spawned_k) A pool with no workers.

## fu_fabric_new

Creates an empty, unharvested memory-fabric handle.

```c
fu_status_t fu_fabric_new(fu_fabric_t *fabric_out);
```

Completes the library's pipeline: build a `[fu_topology_t](#fu_topology_t)` first, spawn a `[fu_pool_t](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_pool_t)` on it, then harvest the fabric through that pool's workers with [fu_fabric_harvest](#fu_fabric_harvest). Before a harvest every query on the handle answers 0, and [`fu_fabric_memory_levels_count`](#fu_fabric_memory_levels_count) answers 1.

- `fabric_out` (out): Receives the opaque fabric handle.

See also: [fu_fabric_harvest](#fu_fabric_harvest), [fu_fabric_delete](#fu_fabric_delete).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL `fabric_out`.

## fu_fabric_delete

Destroys a fabric and frees its observations; the handle is invalid afterward.

```c
void fu_fabric_delete(fu_fabric_t fabric);
```

- `fabric` (in): Fabric handle, may be NULL - a no-op.

Notes: Must not run concurrently with other operations on `fabric`.

## fu_fabric_harvest

Measures the memory fabric through the pool's workers, rebuilding `fabric`.

```c
fu_status_t fu_fabric_harvest(
    fu_topology_t topology,
    fu_pool_t pool,
    fu_fabric_t fabric);
```

Pointer-chases every reachable edge for latency and streams it with each initiator domain's full worker set for bandwidth - in-process, with no ACPI tables or OS-specific interfaces. __Cpuless__ memory domains, like CXL expanders, stay unwalked: portable first-touch cannot place pages there, so their edges answer 0 and they share one tier past the slowest observed.

- `topology` (in): Read only; may be freed once this returns - the fabric snapshots its needs.
- `pool` (in): Pool handle, must not be NULL, spawned across the machine via [`fu_pool_spawn`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_pool_spawn).
- `fabric` (out): Receives the observations, replacing any previous harvest; must not be NULL, and a failed harvest leaves it empty, never half-written.

Notes: Not thread-safe: dispatches on the pool and rebuilds the fabric, so call it between task batches and do not query `fabric` concurrently. Expect seconds of runtime on large fabrics.

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle.
- [`fu_config_mismatch_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_config_mismatch_k) The pool cannot place pages on every domain it would walk - unspawned, terminated, or unpinned on a machine of several domains.
- [`fu_unsupported_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_unsupported_k) This build or machine cannot measure the fabric.

## fu_fabric_memory_latency

Returns the measured read latency from a compute domain to a memory domain.

```c
fu_status_t fu_fabric_memory_latency(
    fu_fabric_t fabric,
    size_t compute_domain_index,
    size_t memory_domain_index,
    size_t *nanoseconds_out);
```

The latency is the best recording of the edge, and 0 before a harvest or for an edge no worker could reach.

- `fabric` (in): Fabric from [`fu_fabric_new`](#fu_fabric_new).
- `compute_domain_index` (in): Initiator compute domain, in [0, `fu_compute_domains_count()`).
- `memory_domain_index` (in): Target memory domain, in [0, `fu_memory_domains_count()`).
- `nanoseconds_out` (out): Receives the dependent-load latency in nanoseconds.

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `nanoseconds_out`.

## fu_fabric_memory_bandwidth

Returns the measured read bandwidth from a compute domain to a memory domain.

```c
fu_status_t fu_fabric_memory_bandwidth(
    fu_fabric_t fabric,
    size_t compute_domain_index,
    size_t memory_domain_index,
    size_t *megabytes_per_second_out);
```

The bandwidth is streamed by all the initiator domain's workers at once, the best recording of the edge, 0 before a harvest or for an edge no worker could reach.

- `fabric` (in): Fabric from [`fu_fabric_new`](#fu_fabric_new).
- `compute_domain_index` (in): Initiator compute domain, in [0, `fu_compute_domains_count()`).
- `memory_domain_index` (in): Target memory domain, in [0, `fu_memory_domains_count()`).
- `megabytes_per_second_out` (out): Receives the saturated read bandwidth in MB/s.

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `megabytes_per_second_out`.

## fu_fabric_memory_distance

Returns the relative access distance from a compute domain to a memory domain.

```c
fu_status_t fu_fabric_memory_distance(
    fu_fabric_t fabric,
    size_t compute_domain_index,
    size_t memory_domain_index,
    size_t *distance_out);
```

The measured latency ratio to the initiator's local domain, clamped so the local domain always carries the row's minimum; unwalked edges fall back to the 10-local / 20-remote convention. __10__ means local per the SLIT convention, and larger is farther.

- `fabric` (in): Fabric from [`fu_fabric_new`](#fu_fabric_new).
- `compute_domain_index` (in): Initiator compute domain, in [0, `fu_compute_domains_count()`).
- `memory_domain_index` (in): Target memory domain, in [0, `fu_memory_domains_count()`).
- `distance_out` (out): Receives the relative distance, __10__ meaning local.

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `distance_out`.

## fu_fabric_memory_level_in

Returns the derived speed class of a given memory domain, independent of any initiator.

```c
fu_status_t fu_fabric_memory_level_in(
    fu_fabric_t fabric,
    size_t memory_domain_index,
    size_t *level_out);
```

Keyed by the best bandwidth any initiator sustains to the pool, ties split by the best latency, so HBM < DDR < CXL/PMEM; boundaries are measurement-derived and can shift between harvests. Tier 0 is the fastest, such as HBM.

- `fabric` (in): Fabric from [`fu_fabric_new`](#fu_fabric_new).
- `memory_domain_index` (in): Target memory domain, in [0, `fu_memory_domains_count()`).
- `level_out` (out): Receives the tier ordinal, __lower__ __is__ __faster__.

Notes: Runs opposite to [`fu_compute_level_in`](#fu_compute_level_in).

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `level_out`.

## fu_fabric_memory_levels_count

Returns the number of distinct derived memory tiers across all memory domains.

```c
fu_status_t fu_fabric_memory_levels_count(
    fu_fabric_t fabric,
    size_t *levels_out);
```

- `fabric` (in): Fabric from [`fu_fabric_new`](#fu_fabric_new).
- `levels_out` (out): Receives 1 on single-tier systems and before a harvest, 2+ when HBM / DDR / CXL are mixed.

Notes: Memory-axis twin of [`fu_compute_levels_count`](#fu_compute_levels_count); several domains may share a tier.

Statuses: - [`fu_invalid_argument_k`](https://www.unum.cloud/docs/forkunion/c/quickstart.md#fu_invalid_argument_k) A NULL handle or a NULL `levels_out`.

