# Topology

> ForkUnion for Mojo

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.

`from forkunion import Fabric, Topology, ThreadInDomain, …`

## Fabric

What ForkUnion measured about this machine's memory, as opposed to what it declares.

```mojo
struct Fabric
```

A [`Topology`](#Topology) holds what the platform reports; a [`Fabric`](#Fabric) holds per-edge latencies, bandwidths,
and distances observed in-process, plus the per-medium tiers derived from them.

- `library` `Library`: The loaded core, held so its symbol table outlives every call made through the handle.
- `handle` `Pointer[NoneType, MutUntrackedOrigin]`: The opaque `fu_fabric_t`, which answers zeroes until a harvest has filled it.

Conforms to: `Deinitable`, `Movable`

## __init__

Allocates an empty fabric; every query answers zero until [`harvest`](#harvest) has run.

```mojo
def __init__(out self, library: Library)
```

- `library`: The loaded core the fabric is allocated through and later queried against.

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the allocation is refused.

## __deinit__

Frees the fabric along with the observations it snapshotted.

```mojo
def __deinit__(deinit self)
```

## harvest

Measures every reachable edge, using the pool to drive the probes. Takes seconds.

```mojo
def harvest(mut self, topology: Topology, pool: Pool)
```

- `topology`: The machine description; snapshotted here, so it may be freed once this returns.
- `pool`: A pool spawned across the whole machine, whose pinned workers run the probes.

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the pool spans no memory domains - flat, pinned to one domain, or terminated.

## memory_latency

Observed access latency from one compute domain to one memory domain.

```mojo
def memory_latency(self, compute: ComputeDomain, memory: MemoryDomain) -> Int
```

- `compute`: The initiating domain, whose worker pointer-chases the edge.
- `memory`: The target domain, whose pages the probe first-touched.

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query; an unharvested edge answers zero rather than failing.

## memory_bandwidth

Observed bandwidth from one compute domain to one memory domain.

```mojo
def memory_bandwidth(self, compute: ComputeDomain, memory: MemoryDomain) -> Int
```

- `compute`: The initiating domain, whose whole worker set streams the edge at once.
- `memory`: The target domain, whose pages the probe first-touched.

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query; an unharvested edge answers zero rather than failing.

## memory_distance

The SLIT-style distance, where a domain's distance to its own memory is 10.

```mojo
def memory_distance(self, compute: ComputeDomain, memory: MemoryDomain) -> Int
```

- `compute`: The initiating domain, whose local memory is clamped to its row's minimum.
- `memory`: The target domain, whose pages the probe first-touched.

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query; an unwalked edge falls back to 10 local, 20 remote.

## memory_level_in

Which performance tier a memory domain landed in, counted from the fastest.

```mojo
def memory_level_in(self, memory: MemoryDomain) -> Int
```

- `memory`: The domain to classify, independently of any initiator.

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query; an unharvested fabric answers zero rather than failing.

## memory_levels_count

Distinct memory tiers this machine turned out to have.

```mojo
def memory_levels_count(self) -> Int
```

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query; an unharvested fabric answers a single tier.

## Topology

The hardware view. Released when the last reference to it goes out of use.

```mojo
struct Topology
```

- `library` `Library`: The loaded core, kept so the release can still reach `fu_topology_delete`.
- `handle` `Pointer[NoneType, MutUntrackedOrigin]`: The `fu_topology_t` this struct owns, non-null for as long as it lives.

Conforms to: `Deinitable`, `Movable`

## __init__

Harvests this machine's description through an already loaded core.

```mojo
def __init__(out self, library: Library)
```

- `library`: A loaded core, kept by the topology so the shared object stays mapped.

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the harvest fails, which a machine that will not describe itself does with [`TOPOLOGY_UNAVAILABLE`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#TOPOLOGY_UNAVAILABLE) and an exhausted allocator with [`BAD_ALLOC`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#BAD_ALLOC).

## __deinit__

Releases the harvested description; the loader reference goes with it.

```mojo
def __deinit__(deinit self)
```

## logical_cores_count

Every hardware thread this machine exposes.

```mojo
def logical_cores_count(self) -> Int
```

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query, which a handle it cannot read does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## logical_cores_count_in

The hardware threads belonging to one compute domain.

```mojo
def logical_cores_count_in(self, domain: ComputeDomain) -> Int
```

- `domain`: A dense compute-domain index, below [`compute_domains_count`](#compute_domains_count).

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the lookup, which an index this machine does not have does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## compute_domains_count

Same-QoS core clusters, each local to one memory domain.

```mojo
def compute_domains_count(self) -> Int
```

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query, which a handle it cannot read does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## compute_levels_count

Distinct core classes; may be fewer than the domains, since domains can share a level.

```mojo
def compute_levels_count(self) -> Int
```

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query, which a handle it cannot read does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## compute_level_in

Which core class a compute domain belongs to, counted from the fastest.

```mojo
def compute_level_in(self, domain: ComputeDomain) -> Int
```

- `domain`: A dense compute-domain index, below [`compute_domains_count`](#compute_domains_count).

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the lookup, which an index this machine does not have does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## compute_capacity_in

A relative throughput weight for one compute domain.

```mojo
def compute_capacity_in(self, domain: ComputeDomain) -> Int
```

- `domain`: A dense compute-domain index, below [`compute_domains_count`](#compute_domains_count).

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the lookup, which an index this machine does not have does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## compute_cache_bytes_per_core_in

Each logical core's share of the deepest cache confined to one compute domain.

```mojo
def compute_cache_bytes_per_core_in(self, domain: ComputeDomain) -> Int
```

- `domain`: A dense compute-domain index, below [`compute_domains_count`](#compute_domains_count).

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the lookup, which an index this machine does not have does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## memory_domains_count

Banks of memory with their own capacity and access cost.

```mojo
def memory_domains_count(self) -> Int
```

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query, which a handle it cannot read does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## local_memory_of

The memory domain nearest a compute domain, as a dense index.

```mojo
def local_memory_of(self, domain: ComputeDomain) -> MemoryDomain
```

This is an index, not an id. Pass it through [`memory_domain_id_at_index`](#memory_domain_id_at_index) before handing it
to an allocator, which keys off the operating system's own numbering.

- `domain`: A dense compute-domain index, below [`compute_domains_count`](#compute_domains_count).

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the lookup, which an index this machine does not have does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## memory_domain_id_at_index

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

```mojo
def memory_domain_id_at_index(self, domain: MemoryDomain) -> MemoryDomainId
```

A real domain may itself carry -1 where the OS names none, so an index this machine does
not have is a reported refusal rather than that same value.

- `domain`: The dense memory-domain index to resolve, below [`memory_domains_count`](https://www.unum.cloud/docs/forkunion/mojo/numa.md#memory_domains_count).

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the lookup, which an index this machine does not have does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## volume_ram

Bytes of RAM installed, regardless of page size.

```mojo
def volume_ram(self) -> Int
```

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query, which a handle it cannot read does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## volume_ram_in

Bytes of RAM in one memory domain.

```mojo
def volume_ram_in(self, domain: MemoryDomain) -> Int
```

- `domain`: A dense memory-domain index, below [`memory_domains_count`](https://www.unum.cloud/docs/forkunion/mojo/numa.md#memory_domains_count), never an OS id.

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the lookup, which an index this machine does not have does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## volume_huge_pages

Bytes backed by free huge pages across every memory domain.

```mojo
def volume_huge_pages(self) -> Int
```

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query, which a handle it cannot read does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## volume_huge_pages_in

Bytes backed by free huge pages in one memory domain.

```mojo
def volume_huge_pages_in(self, domain: MemoryDomain) -> Int
```

- `domain`: A dense memory-domain index, below [`memory_domains_count`](https://www.unum.cloud/docs/forkunion/mojo/numa.md#memory_domains_count), never an OS id.

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the lookup, which an index this machine does not have does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## huge_pages_count

Free huge pages of any size across every memory domain.

```mojo
def huge_pages_count(self) -> Int
```

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the query, which a handle it cannot read does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## huge_pages_count_in

Free huge pages of any size in one memory domain.

```mojo
def huge_pages_count_in(self, domain: MemoryDomain) -> Int
```

- `domain`: A dense memory-domain index, below [`memory_domains_count`](https://www.unum.cloud/docs/forkunion/mojo/numa.md#memory_domains_count), never an OS id.

Raises: [`Error`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#Error) if the core refuses the lookup, which an index this machine does not have does with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/forkunion/mojo/quickstart.md#INVALID_ARGUMENT).

## ThreadInDomain

One thread, situated in one compute domain - the "where I am" every callback receives.

```mojo
struct ThreadInDomain
```

Every dispatch hands its callback two things: the work, and this. A callback placing memory
reads `compute_domain` to find the node it runs on; one indexing per-thread scratch reads
`thread`.

- `thread` `Int`: The physical thread executing the work.
- `compute_domain` `Int`: The compute domain - a same-QoS core cluster - the thread runs on.

Conforms to: `Copyable`, `Deinitable`, `ImplicitlyCopyable`, `Movable`, `RegisterPassable`, `TrivialRegisterPassable`

## ComputeDomain

A dense index into this machine's compute domains, counted from zero.

```mojo
struct ComputeDomain
```

- `index` `Int`: The zero-based position among this machine's compute domains.

Conforms to: `Copyable`, `Deinitable`, `Equatable`, `ImplicitlyCopyable`, `Movable`, `RegisterPassable`, `TrivialRegisterPassable`, `Writable`

## write_to

Writes the index with the kind of domain spelled out, so a log line cannot be misread.

```mojo
def write_to(self, mut writer: T)
```

- `writer` (mut): The sink the label is appended to.

## MemoryDomain

A dense index into this machine's memory domains, counted from zero.

```mojo
struct MemoryDomain
```

- `index` `Int`: The zero-based position among this machine's memory domains.

Conforms to: `Copyable`, `Deinitable`, `Equatable`, `ImplicitlyCopyable`, `Movable`, `RegisterPassable`, `TrivialRegisterPassable`, `Writable`

## write_to

Writes the index labeled as a dense one, never bare, so it cannot pass for an OS id.

```mojo
def write_to(self, mut writer: T)
```

- `writer` (mut): The sink the label is appended to.

## MemoryDomainId

The operating system's own id for a memory domain, which is not an index.

```mojo
struct MemoryDomainId
```

The allocators key off this rather than off the topology, so an allocation can outlive the
handle that found it. Reach it through [`Topology.memory_domain_id_at_index`](#memory_domain_id_at_index), never by reusing
a dense index.

- `identifier` `c_int`: The id the operating system uses, which need not match any dense index.

Conforms to: `Copyable`, `Deinitable`, `Equatable`, `ImplicitlyCopyable`, `Movable`, `RegisterPassable`, `TrivialRegisterPassable`, `Writable`

## write_to

Writes it as the NUMA node the operating system itself calls it.

```mojo
def write_to(self, mut writer: T)
```

- `writer` (mut): The sink the label is appended to.

