Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Machine files

Most compilers hard-code a cost model. Vx reads one.

A machine file describes the memory hierarchy and interconnect of a real part. The compiler admits or rejects your placements against it, and derives transfer costs from it, before a binary exists.

An example

#![allow(unused)]
fn main() {
Memory HBM  { capacity: 80 GiB, bandwidth: 3.35 TB/s, managed: explicit, scope: device }
Memory L2   { within: Memory::HBM, capacity: 50 MiB, bandwidth: 12 TB/s, managed: cached }
Memory SMEM { within: Memory::L2, capacity: 228 KiB, bandwidth: 128 B/cyc,
              clock: 1.98 GHz, replicas: 132, granule: 1 KiB, scope: sm }

Topology Device {
    arch: nvptx64,
    memory: Memory::HBM,
    transfer Memory::CPU_DRAM -> Memory::HBM : 63 GB/s,
}
}

That is an H100, in eleven lines.

The fields

capacity — how much the space holds. Checked against the working set of anything you place there.

bandwidth — either a rate (3.35 TB/s) or a per-cycle figure with a clock (128 B/cyc at 1.98 GHz). The second form is how scratchpads are usually specified in vendor documentation.

within — containment. L2 sits inside HBM. The containment relation must be acyclic, a child may not exceed its parent’s capacity, and scope narrows as you descend.

managed — explicit if software moves the data, cached if hardware does.

scope — who can see it. device is visible to the whole accelerator; sm is private to one streaming multiprocessor.

replicas — how many copies of the space exist. 132 SMs means 132 scratchpads, and a placement is admitted against one of them, not their sum.

granule — the allocation quantum. A 1 KiB granule means a 100-byte tensor occupies 1 KiB, and admission rounds accordingly.

node — which NUMA domain of the host this space’s memory physically is. The only field here a backend acts on rather than checks against: a placement into a space that declares one is bound to that node, and a dispatch reading it pins itself there. Absent on every accelerator in fleet/, because a GPU has one memory and nothing to choose between. Zero is a real node rather than an absence. See NUMA and the host’s memory domains.

Units are exact

SI prefixes are decimal and IEC prefixes are binary:

GB10⁹ bytes
GiB2³⁰ bytes
TB/s10¹² bytes per second

Conversions are exact integer arithmetic, never floating point. A figure copied off a vendor datasheet means precisely what the datasheet meant, and does not drift by a fraction of a percent on the way through the compiler.

What the compiler derives

From that declaration alone, before any code is generated:

Admission — whether a tensor’s working set fits the space it is placed in, with granule rounding applied. A rejection carries the space, the amount required, the amount available and the margin.

Routing — the cheapest legal path between two spaces, over the declared transfer graph. If you transfer from a space that has no declared route to the destination, that is an error rather than a silently inserted staging copy.

Transfer cost — a roofline over the containment tree. A containment hop is charged at both endpoints, because data has to leave the parent as well as enter the child.

Coherence of the model itself — within is acyclic, no child exceeds its parent, scope narrows downward, and every edge has exactly one cost source. A machine file that contradicts itself is rejected as a machine file, before your program is even considered.

The bundled fleet

The repository ships machine files for real parts under fleet/: H100, H200, B200, A100, MI300X, Apple M4 unified memory, multi-GPU nodes and hosts. Each one cites its sources, and marks figures that have not been validated against hardware as unverified.

That last part matters. A declared bandwidth is often the vendor’s hardware peak rather than an achievable rate — Apple’s 120 GB/s for the base M4 is the memory system’s ceiling, not what a copy loop will reach. The fleet files state the vendor’s claim rather than a number already fitted to a measurement, so the gap between prediction and reality stays visible instead of being quietly tuned away.

Using one

vxc --machine fleet/h100-sxm.vx program.vx -o program

--machine prepends the declarations to your compilation unit. They are visible to capacity and placement checks exactly as if you had written them inline. A name declared by both the machine file and the program is an error, never a silent shadow.

--machine describes an accelerator and says nothing about the host it hangs off. If your program stages through host memory, declare the host too:

vxc --machine fleet/h100-sxm.vx --host default program.vx -o program

--host default means “the machine doing the compiling”. A host file declares no capacity, on purpose: host memory is virtual, and a tensor larger than physical RAM pages rather than failing. A hard limit there would reject programs that actually run.

Writing your own

Start from the closest file in fleet/ and change the numbers. The compiler will tell you if the result is incoherent, which makes the edit-check loop fast. Two habits worth keeping:

  1. Cite every figure. The fleet files carry the source of each number in a comment. When a prediction is wrong, the first question is always whether the model or the measurement is at fault, and a citation answers it in seconds.
  2. Mark what you have not verified. A number from a datasheet and a number from a benchmark are different kinds of thing, and the difference should survive in the file.