Plow mark — a plough blade turning three furrows Plow mark — a plough blade turning three furrows
PRODUCT · PLOW

The compiled
inference runtime

Plan the model before deployment. Keep the request path small.

Plow — a Packet Language for On-device Workers, where a worker is a warp on NVIDIA and a wave on AMD — is the engine inside Infervisor. The compiler turns a model into a validated, target-specific execution artifact ahead of time; at request time a small Rust runtime — plowrt — loads that artifact and executes it on NVIDIA or AMD accelerators.

The artifact is not a graph the runtime still has to interpret decisions from. It is an instruction stream for the accelerator itself, with the tile shapes chosen, the on-chip memory assigned, the dependencies expressed as atomic counters, and every address resolved against one arena. Prefill, decode, sampling, key-value paging, and cross-GPU collectives are all entries in that same stream. Nothing about a token requires the host to decide what runs next.

+0.9%
measured serving overhead over the raw kernel on a 12B model
off-path
host CPU decisions during the steady-state model execution loop
1 runtime
contract for NVIDIA and AMD, with multi-model pipelines in scope

Evidence key: the first figure is measured; the other two are architectural properties. Cold-start and multi-model gains remain evaluation targets, not published benchmark claims.

From checkpoint to artifact

Most serving stacks repeatedly decide, dispatch, and synchronize work while a request is already waiting. Plow's premise is that if the model, target, and operating profile are known before deployment, the runtime should receive a prepared execution artifact rather than reconstructing a plan for every process or request.

The system therefore splits into two phases. Before deployment, the compiler imports the model, considers equivalent graph forms, chooses a target-specific plan, validates it against a reference, and packages the result. At request time, plowrt loads the artifact and executes it. The important boundary is when the work happens.

The compile / serve boundary

expensive reasoning belongs before deployment; the request path should load, execute, and report

Plow moves optimization work out of the realtime path A model passes through import, optimization, planning, validation, and packaging before deployment. The prepared artifact crosses the compile/serve boundary into a small runtime that serves live sessions on NVIDIA or AMD accelerators. A timeline shows the realtime contract: fast first token, then a steady per-token cadence inside a p99 latency budget. Measurements feed back into the compiler. BEFORE DEPLOYMENT pay the planning cost once, before any session starts 01 Import model + shapes 02 Optimize whole graph 03 Plan target profile 04 Validate correctness gate 05 Package ready artifact COMPILE / SERVE BOUNDARY prepared artifact AT SESSION TIME — REALTIME LIVE SESSION stream in mic · tokens · frames LIGHTWEIGHT RUNTIME plowrt execute the prepared plan no decisions left on the hot path ACCELERATOR NVIDIA · AMD same compiled-plan contract steady per-token cadence — no scheduler jitter request t₀ first token p99 budget

The feedback loop is empirical: profile, verify output correctness, and refine the compiler. A legal plan is not assumed to be a fast plan.

What the compiler decides before you deploy

The compile phase reads a HuggingFace checkpoint directory — config, safetensors shards, tokenizer — and lowers it to a shape-inferred operator graph. Every tensor's dimensions are known from that point on, which is what makes the rest of the pipeline possible: a tile graph is assembled, each matrix multiply and attention block is cut into tiles whose shapes are chosen against a hardware cost model, and the tiles are placed with L2-partition-aware scheduling. On chiplet accelerators like CDNA4 each die owns a slice of L2, so tiles and their traffic are mapped to stay resident in a local partition rather than crossing dies.

The scheduler then does the part that has no equivalent in a launch-based runtime. It assigns every dependency in that graph to an atomic counter — producers increment, a consumer proceeds when the count reaches its threshold — and it picks the cheapest scope each counter can legally use: a shared-memory barrier when producer and consumer land on the same SM, a device-scope atomic when they share a GPU, a system-scope atomic only when they do not. It assigns on-chip memory as pages with a lifetime, hoists loads earlier to overlap with prior compute, and resolves every address as an offset into one arena, so there is no allocator on the request path. The emitter writes the result as the packet stream, together with a ladder of compiled buckets — one per (phase, batch, sequence) rung — that the runtime rounds each live shape up into.

There is also an egglog equality-saturation stage: nineteen shape-preserving fusion rules over one e-graph, saturated and extracted by cost, each rule carrying a machine-checked soundness theorem. We should be precise about its status. The search runs and reports what it finds, but it is advisory today — the fusions that reach a packet are the ones the emitter implements directly, and when we measured what routing the search's output through the emitter would buy on a tuned decode path, the answer was below the step-to-step noise. It is a real optimizer on a real e-graph; it is not currently what makes plow fast, and saying otherwise would be easy and wrong.

Every stage above is gated by a Lean oracle: six checkpoints (A–F) covering rule soundness, tile partition and cost, on-chip memory fit, the counter protocol, the wire round-trip, and allocation safety. That deserves more than a sentence — what the proofs actually prove is a section of its own below.

The proof-carrying compile path

egglog e-graph rewrite → schedule → packet stream, with a Lean oracle rail gating each stage it certifies

The plow compile path, gated by six Lean checkpoints A–F, emitting a packet stream A model config and weights lower to a shape-inferred operator graph. An egglog e-graph saturates the fusion rules and extracts the lowest-cost equivalent form. The tile graph is assembled with L2-partition-aware placement for chiplet GPUs, the schedule places counters and SRAM, and the emitter writes the on-device packet stream the runtime executes. A Lean oracle rail runs six checkpoints: A certifies the rewrite rules are sound, B tile partition and cost, C SRAM temporal fit, D the counter protocol, E the wire round-trip, and F allocation safety. It proves the legality of every compiler step, not its speed. COMPILE — BEFORE DEPLOYMENT config + weights any model · quant operator graph shapes inferred e-graph rewrite egglog · fusion rules tile graph L2-partition-aware schedule counters · SRAM emit packet stream LEAN ORACLE proves legality of every compiler step A rules sound B tiles + cost C SRAM fit D counters E wire I/O F alloc safe

The Lean oracle proves the legality of every compiler step — not its performance; no theorem, no rule. The same path holds on NVIDIA and AMD; only the emitted packet stream is target-specific.

Every phase of a request, in one plan

A request is not one workload. Reading the prompt saturates the arithmetic units; generating each subsequent token barely uses them and is limited instead by how fast weights can be pulled from memory. Serving stacks usually paper over that with one dynamic scheduler that handles both. Plow compiles them as separate buckets of the same bundle, so each phase gets a plan shaped for what actually limits it, and the runtime's job is to pick the rung of the ladder that covers the live shape.

Prefill and decode, as the runtime executes them

chunked and packed on the way in; one dispatch per token on the way out

The two phases of a request: chunked prefill and one-dispatch-per-token decode Both phases are served from one compiled bundle, a ladder of phase, batch and sequence buckets that the runtime rounds each live shape up into. Prefill is compute-bound and chunked: each chunk is one launch, packed together with the chunks of other waiting requests, and it writes the key-value cache in place. Decode is memory-bound and is a single dispatch per token: a persistent kernel walks a few hundred counter-gated packets, and the sampled token identifier is written on the device straight into the next step's embedding input, so the host makes no per-operation decision inside the loop. ONE COMPILED BUNDLE — A LADDER OF (PHASE, BATCH, SEQUENCE) BUCKETS; THE RUNTIME ROUNDS THE LIVE SHAPE UP INTO IT PREFILL compute-bound · one launch per chunk · chunks of different waiting requests packed into the same launch chunk 1 rows → KV cache chunk 2 rows → KV cache chunk 3 rows → KV cache chunk k rows → KV cache ··· A launch is not free — it re-reads every layer's weights — so the compiler prices one in padded rows and the runtime picks the chunking that minimises total cost, rather than a fixed chunk size. The prompt's keys and values land directly in the cache; no separate copy. DECODE memory-bound · one dispatch per token · no host decision inside the loop one persistent kernel walks a few hundred counter-gated packets sample on device the sampled token id is written on the device into the next step's embedding input — the host is not asked what to run next Counters are the only coordination primitive: producers increment an atomic integer, a consumer proceeds when it reaches its threshold.

Green dots are counters. They are the only coordination primitive in the system — the same mechanism inside an SM, across SMs, and across GPUs, at three different scopes.

Prefill — chunked, packed, and priced

Long prompts are processed in chunks so that a single request cannot monopolise the device, and the chunks of several waiting requests are packed into one launch under a token budget. The chunk size is not a constant we picked. A launch is not free — it re-reads every layer's weights from memory and pays grid and counter setup — so the compiler prices one launch in padded-row equivalents, measured by regressing time-to-first-token over a prompt-length sweep, and the runtime chooses the chunking that minimises total cost rather than the one that minimises padding. The prompt's keys and values are written straight into the cache at their destination row as part of the attention packet; there is no separate copy pass.

Prefill is also where our honest gap is. It is the phase we have tuned least, and it is first in the auto-tuner's queue. We publish decode results today and will publish prefill results when they have been through the same correctness-gated evaluation, not before.

Decode — one dispatch, and the host is not consulted

A decode step is a single dispatch. Inside it, a persistent kernel walks a few hundred counter-gated packets: the bandwidth-bound linear layers, attention split across workers with a merge step so that a batch of one still occupies the whole GPU, the router and expert arithmetic if the model is a mixture of experts, and the output projection. Then it samples. Greedy sampling is a reduction fused into the output-projection packet; temperature, top-k, top-p and min-p run in a device sampler with per-row parameters, so a batch can mix greedy and stochastic requests. Either way the chosen token id is written on the device directly into the tensor the next step's embedding lookup reads. An optional multi-step mode advances a fixed quantum of tokens with one host round trip instead of one per token.

The key-value cache is a paged block pool with per-request page tables, in the manner PagedAttention established. Plow stores it head-major, so a shared prompt prefix is not one block but a set of strided runs at the same offset in every head's slot — which is what its prefix cache indexes. Above that, a shared prefix can be held once in physical memory and multi-mapped into each sharing sequence's virtual window, so the second and later users of a long shared prompt cost address space rather than gigabytes. Cache precision is bf16 by default; an fp8 key-value mode exists, is opt-in, and is lossy — we say so rather than quoting the memory saving alone.

The serving loop around them

Each model has a dispatcher holding a slot table sized to the largest compiled batch. Between decode ticks it admits new arrivals into free slots, so a request that arrives mid-generation gets its first token without waiting for the in-flight ones to finish. Tokens stream out as incremental detokenized deltas, and a client that disconnects frees its slot on the next tick.

Admission control is where being static pays off in an unexpected place. Queueing theory needs a service-time distribution, and a dynamic runtime can only estimate one by observing itself. Plow knows the packet count and the byte cost of a decode step before any request arrives, so the service time is a lookup — and because a compiled step is deterministic to well under a percent step-to-step on the model and card we measured it on, the arrival model that applies is the deterministic-service one rather than the exponential-service one usually assumed. The batch-formation window becomes the minimiser of a latency function instead of a tuned constant. That is the mechanism behind the predictable-tail claim, and it is a design property we can point at rather than a number we would like to be true.

What the plan has to cover

"Compile the model" is only interesting if the compiler covers the shapes models actually take. Modern architectures are not uniform stacks of the same attention block, and a compiled runtime has to express the variation without a special case per checkpoint.

ATTENTION

Four families, one stream

Multi-head and grouped-query attention, with the sliding window as a kernel parameter so a model that interleaves local and global layers compiles as one plan. Multi-head latent attention for models that cache a compressed latent instead of keys and values. Sparse selection, where an indexer scores the cache and attention gathers only the entries a query needs. And gated linear-attention and state-space blocks, for hybrids that mix a recurrent block with full attention layer by layer.

MIXTURE OF EXPERTS

Routing is packets, not a detour

The router, the grouped expert arithmetic, and the combine are ordinary counter-gated entries in the same stream. The router's completion is a counter, so expert work can begin on the workers that are ready without a host round trip between the routing decision and the arithmetic it selects.

PRECISION

Chosen per operator, checked per checkpoint

bf16 throughout; fp8 weights with per-channel scales; fp8 activations with per-row scales; block-scaled fp8 for checkpoints that ship that way; and 4-bit block-scaled expert weights on CDNA4. The compiler reads the checkpoint's own quantization metadata and refuses a mismatch rather than silently reinterpreting bytes.

MULTI-GPU

A collective is an instruction

In a launch-based runtime every all-reduce in a decode step is a launched library kernel with a host synchronisation, and at batch one those dominate. In plow a collective is more entries in the resident kernel's stream, gated by counters in peer-mapped memory. Tensor parallelism is measured across eight accelerators; expert and pipeline partitioning are expressed by the same pass.

Where each model family actually stands

Coverage claims are easy to inflate, so here is the ladder we hold ourselves to. Served and measured on real accelerators: Gemma-4 (12B and 31B dense, 26B-A4B mixture-of-experts), Qwen3, Llama-3.1, and Kimi-K3 — a hybrid that mixes latent attention with linear-attention layers and 4-bit experts, brought up and measured across eight CDNA4 accelerators. Compiles a block at a time, not yet a served model: other latent-attention checkpoints of the DeepSeek lineage, and Mamba-2 hybrids, which have a device operation but no model-level validation.

Graph only, with no device path: vision towers and image-diffusion transformers. Their graphs build and shape-infer, and several of the fusion rules exist specifically for them — adaptive layer-norm modulation, gated residuals, group-norm-and-activation chains. But there is no device operation for convolution or group normalisation, no image decoder, and no diffusion sampler, so nothing about them runs. Design only: speech recognition and synthesis. The multi-stage pipeline abstraction that would carry an encoder into a decoder on-device exists in the runtime and is not yet reachable from the serving loop. We describe those as where the architecture is going, and we do not describe them as features.

What the proofs actually prove

GPU coordination bugs have a specific and unpleasant character. A counter with the wrong threshold does not throw; it produces slightly wrong tokens, or it hangs, and which one it does depends on timing. Unit tests do not find that class, because the failing interleaving is the one the test machine did not happen to run. So plow carries a Lean 4 library that proves the properties instead of sampling them.

It works by proving universal theorems once and applying them per compilation. For each check the compiler serialises a payload — the counter graph with its producers, thresholds and scopes; the tile shapes; the on-chip memory timeline; the encoded packet bytes; the address map — and invokes a separate Lean binary that checks this instance's preconditions and returns a certificate naming the theorems it applied. Running it as its own process rather than a linked library means a prover crash cannot corrupt a compile, and it means the checks can be switched on and off. They are opt-in at the command line and always on in continuous integration. There is a deliberately broken schedule in the test suite whose job is to be rejected; a verifier that only ever says yes is not a verifier.

The interesting part is not the first column of the table below. Proving a compiler's output legal is the expected use of a theorem prover. The second column is the one that changes how the compiler is written: a proof that an optimisation is safe is a licence to take it without re-arguing it per model, and a proof that an optimisation cannot pay is permission to stop working on it. Both kinds are in the library, and the second kind has saved us more engineering time than the first.

The proof library, by what each theorem buys

counters and tiles — the two objects the compiler reasons about — against legality and speed

Counters

the on-device dataflow

FASTER
  • tr_preserves_coverage · dedup_preserves_coveragedropping a wait that a stronger edge already implies, or merging two counters into one, provably keeps every dependency covered. This is the licence for the elimination and dedup passes.
  • protocol_covers_deps_after_reorderhoisting a load earlier in the stream preserves the whole protocol — so prefetch is free to move, and the pass does not have to be re-argued per model.
  • collapse · fineCanPay_false_of_uniformwhen a stage’s slices do equal work, per-slice counters cannot beat one barrier counter — the straggler’s slack reaches the barrier either way. The compiler runs this as a decision procedure and declines the finer counters.
  • hetero_can_winthe witness that the rule above is not vacuous: an explicit uneven case where fine counters strictly win, 11 against 18.

Tiles

the arithmetic and the on-chip memory

FASTER
  • tile_work_bounds_gemmthe work the cost model charges a tiling is never less than the arithmetic it really performs, so a tiling that looks cheaper is cheaper.
  • makespan_ge_hbm_bound · makespan_ge_compute_bound · makespan_dominates_lower_boundsa proved roofline. A candidate schedule can be discarded against a lower bound instead of being built and measured.
  • infeasible_when_bw_saturated · infeasible_when_compute_saturatedthe contrapositive: a plan that would need more bandwidth or more arithmetic than the machine has is rejected at compile time, with a reason.
  • persistent_weight_saves · decode_weight_equals_idealthe reason decode is served from one resident plan, stated as a theorem about weight traffic rather than as a benchmark.
  • inPlace_equals_outOfPlace · streaming_equals_materializing · disjoint_writes_commutewriting into a buffer already live, and streaming a value instead of materialising it, are equalities — so the emitter may take them without a numerical argument.
  • split_k_two_way · interleave_correctsplitting a reduction across workers and interleaving a fused layout both return the original value.
37proof modules
184theorems and lemmas
6,130lines of Lean
0sorry, admit, or added axioms

Theorem names are the real identifiers in the Lean sources. The library builds with strict elaboration and contains no sorry, no admitted lemmas, and no axioms of its own.

Counters: proving a dataflow, then pruning it

The verification side is the part you would expect. A counter protocol is well-formed when every dependency edge in the task graph is covered by some counter whose threshold is exactly the number of producers assigned to it, and the relation those gates induce is acyclic. From that, deadlock-freedom is a theorem rather than a soak test, and it holds for every schedule the compiler can emit, not just the ones we ran.

The optimisation side is where the proofs earn their place. Three passes rewrite the counter graph after scheduling. One removes counters whose dependency is already implied by the order two tasks occupy on the same resource. One merges counters that carry the same dependency. One hoists loads earlier so they overlap with prior compute. Each of those is a transformation that could silently break the dataflow, and each has a theorem saying the coverage property survives it — so the pass is written once and trusted, rather than re-validated per model with benchmarks that would not detect the failure anyway.

Then there is a result that told us not to build something. The natural next idea is finer counters: instead of one gate saying "all producers of this operator are done", give each consumer slice its own gate so it can start as soon as its own producers finish. The collapse theorem says that for a chain of counter-gated stages ending in a barrier, the finish time is the latest arrival plus the total work, and that expression does not mention which producers each consumer waited on. The straggler's slack reaches the barrier regardless. Fine counters can only win when the consumer slices do unequal work — and the library carries the smallest witness of that too, so the rule is not vacuously true. The compiler runs the corollary as an executable decision: it asks whether the cost model gives this operator's slices differing costs, and on a model whose attention heads are all identical the answer is no on every edge, so it emits the coarse counters. That matches what we measure. On a mixture-of-experts model the answer can be yes, and the finer counters remain available.

A measurement closes the loop. On a 48-layer 12B model compiled in fp8, the emitted graph has exactly one counter per operation, and its transitive reduction is itself — 717 dependency edges, 717 after reduction, and a single dead counter in the whole model. The passes that the proofs made safe to run have nothing left to remove. That is a negative result, and it is worth more than a speedup, because it closed a line of work instead of starting one.

Tiles: proving a partition, then pricing it

On the tile side, the legality question is whether the chosen tile shape actually covers the matrix multiply it claims to. The theorem is a completeness statement: the tile steps cover the whole index space. It is stated to permit an over-sized tile on a small operator — a router with eight output columns compiled against a minimum tile width of sixteen — where one tile covers the axis with masked waste and the coverage argument still holds. That case is common enough that a theorem which quietly excluded it would be worse than none. Alongside it is a soundness result for the executable check the compiler calls, so the fast decision procedure and the proved property cannot drift apart. On-chip memory gets a temporal argument: if a producer's window closes before its consumer's opens and each side individually fits the page budget, then occupancy never exceeds the budget at any cycle — with a counterexample proving both halves of that hypothesis are load-bearing.

The optimisation side is a proved cost model. The work a tiling is charged is never less than the arithmetic it really performs, so choosing the cheaper-looking tiling is choosing the cheaper one. Above that sit lower bounds on the schedule's makespan from the memory bandwidth and the arithmetic throughput of the machine, and their contrapositives: a plan needing more bandwidth or more compute than the hardware has is rejected at compile time with a reason, rather than built and measured. That is what makes exploring a large tile space affordable — most candidates are eliminated by arithmetic, not by running them. The same file proves the weight-traffic argument for decode that justifies keeping the plan resident, and separate modules prove that writing in place, streaming a value rather than materialising it, splitting a reduction across workers, and the interleaved layout the fused gated-MLP uses are all equalities. Those are exactly the rewrites where a heuristic would be plausible and wrong.

And what the proofs do not do

Lean proves the plan is legal. It does not prove the plan is fast — speed is a separate, measured gate, and a schedule can pass every checkpoint and still lose to a better one. It does not prove the kernels themselves compute the right function; those are gated numerically against a reference, and a family without a correctness oracle cannot produce a qualified measurement at all. The on-chip memory checkpoint is defence in depth over a scheduler pass that already enforces the same invariant, which is why it is opt-in and expensive rather than always on. And the argument for splitting the instruction stream into segments with different occupancy is written down and not yet mechanised — it is stated in the design notes as a proof obligation, and until it is discharged we describe it as an argument, not a theorem.

A runtime built to stay out of the way

The serve-time surface is deliberately narrow, but the consequences span the serving system. The diagram shows where each property lives in the stack; the cards below distinguish what is measured today from what is still a hypothesis under test.

Inside the serving stack

one prepared plan, one small serving surface, fewer decisions after a request arrives

Cross-section of the Plow serving stack Requests enter a thin plowrt runtime layer, which executes resident compiled model plans — speech recognition, language model, and speech synthesis stages — on NVIDIA and AMD accelerators. The host CPU sits outside the steady-state loop and cold start is an artifact load. INCOMING REQUESTS text image audio plowrt the entire serve-time surface deliberately small — no compile work here CPU host cpu — outside the loop × 1 RESIDENT MODEL PLANS 4 PLAN asr speech → text PLAN llm generate PLAN tts text → speech 5 ARTIFACT 3 cold start = load, not compile 2 no control-plane round-trips per step NVIDIA AMD 6 one compiled-plan contract across vendors
  • 01 Host CPU off the hot path — no per-step orchestration in the steady-state loop
  • 02 Fewer sync boundaries — dependencies are resolved in the plan, not at run time
  • 03 Fast cold-start path — starting a model means loading an artifact, not compiling
  • 04 Multiple resident plans — several models live behind one serving surface
  • 05 Pipeline stages — model-to-model hand-offs stay inside the runtime
  • 06 NVIDIA + AMD — one compiled-plan contract across vendors

The speech-in and speech-out stages show the shape the pipeline abstraction is built for, not a shipped capability: several language-model plans co-resident behind one serving surface is measured, and stage-to-stage hand-off inside the runtime is not yet reachable from the serving loop.

MEASURED

Lightweight serving path

The Rust serving layer adds 0.9% over the raw kernel on a 12B model and falls below measurement noise on the larger models in our current Blackwell study.

ARCHITECTURAL

Host CPU savings

The steady-state model plan does not require the CPU to choose every next operation. We describe this as removal from the hot path, not as a CPU-percentage claim; host-utilization measurements will be published separately.

ARCHITECTURAL

Lower synchronization overhead

Preparing dependencies ahead of time reduces the number of control-plane boundaries during execution. It does not remove synchronization inherent to the model or the hardware.

ARCHITECTURAL

Consistent p99, predictable system

A fixed execution plan runs the same way every token — no per-request scheduling decisions to introduce jitter. That keeps the per-token cadence steady and the tail (p99) latency consistent, and it makes the overall system's timing predictable to reason about and provision, rather than a distribution that shifts with load.

ARCHITECTURAL

Compile-time optimization

The compiler can evaluate the whole model and its deployment target together, then reuse the chosen artifact. This creates room for global decisions that a request-time optimizer cannot afford to revisit.

EVALUATING

Fast cold starts

A prepared artifact avoids first-request compilation and reduces framework initialization work. Weight transfer and device initialization still exist; time-to-first-ready benchmarks are not yet published.

EVALUATING

Multiple models and pipelines

The same runtime can represent several model plans and connect stages such as speech recognition, language generation, and speech synthesis. Co-residency, hand-off latency, and isolation are active measurement areas.

Every kernel is ours

Plow has no vendor math or collective library underneath it. There is no cuBLAS, cuDNN, CUTLASS, NCCL, or ROCm/RCCL anywhere in the serving path. Every kernel plow runs — the GEMMs, attention, the Mixture-of-Experts routing, and the cross-GPU all-reduce — is written by us. On NVIDIA they compile with nvcc; on AMD, with AMD's clang. The runtime binds directly to the CUDA driver API on NVIDIA and to the HSA runtime on AMD — nothing else sits between plow and the accelerator.

The serving stack — plowrt to the metal

one process serves the OpenAI endpoint and binds straight to the driver; our AOT kernels side-load in

The plow serving stack: plowrt on the driver API, no vendor libraries between A block diagram of the plow serving stack. HTTP clients call an OpenAI-compatible endpoint that plowrt serves directly. plowrt is one host process holding the runtime, scheduler and every kernel; it binds straight to the user-space driver — the CUDA driver API on NVIDIA, libhsa the HSA runtime on AMD — with no cuBLAS, NCCL, RCCL or framework in between. Below the driver sits only the OS kernel driver (nvidia.ko or amdgpu.ko) and then the GPU, where the persistent megakernel runs the compiled plan. Our own kernels, compiled ahead of time with nvcc or AMD clang into .cubin and .hsaco modules, are side-loaded into the running process at startup. HTTP clients OpenAI-compatible endpoint /v1/chat/completions requests HOST · one process plowrt serving · scheduler · every kernel — one Rust binary, no Python stack no cuBLAS · cuDNN · NCCL · RCCL CUDA driver API NVIDIA · user-space libhsa — HSA runtime AMD · user-space the only layer beneath us kernel driver nvidia.ko / amdgpu.ko — OS kernel ↔ device submit · DMA GPU — NVIDIA Blackwell · AMD CDNA persistent megakernel resident — runs the compiled plan AOT ARTIFACTS nvcc / AMD clang .cubin — NVIDIA .hsaco — AMD side-loaded into plowrt at startup

Nothing between plowrt and the driver: the CUDA driver API on NVIDIA, libhsa (HSA runtime) on AMD, then the OS kernel driver and the GPU. Kernels are ours — compiled ahead of time with nvcc / AMD clang into .cubin / .hsaco and side-loaded at startup.

This is a deliberate cost, and it is the point. The same compiled execution plan, the same counter protocol, and the same collectives run on both vendors with no dependency on a library we don't control — which is why AMD is a first-class target rather than a port, and why a collective in plow is just another entry in the resident kernel's stream instead of a launched library call.

How we evaluate it

A systems rewrite is only useful if it survives comparison. We report results by workload phase because prefill and decode stress different parts of the machine, and we avoid collapsing latency, throughput, startup, and efficiency into one headline number.

  1. 01
    Fix the workload.

    Model, precision, context, batch size, tensor-parallel degree, hardware, and software versions are recorded per run.

  2. 02
    Gate on correctness.

    Output is checked against a reference before performance data is accepted.

  3. 03
    Separate the surfaces.

    Raw execution, serving overhead, cold start, host utilization, and end-to-end pipeline latency are measured independently.

  4. 04
    Publish limits.

    Untuned paths, crossovers, and unmeasured design goals are stated next to the results rather than hidden in footnotes.

The first published decode studies follow this protocol. On Blackwell, serving overhead measured +0.9% on the 12B model and below noise on larger models. On 8× MI350X, Plow's per-token latency grew 1.40–1.52× from 1k to 128k context, compared with 1.9–2.9× for the tested vLLM configurations. Those results support a low-overhead execution path; they do not yet prove the cold-start or multi-model hypotheses.

What we are not claiming

  • Plow does not make data transfer, weight loading, or device initialization disappear.
  • Ahead-of-time compilation shifts work earlier; it does not make compilation free.
  • Lower host involvement does not mean zero host CPU use across networking, tokenization, and the serving API.
  • Multi-model support is a runtime design property today; production co-residency results are still pending.
  • Current public measurements cover LLM decode. Diffusion, voice, prefill, and multi-user throughput will be published only after the same correctness-gated evaluation.
  • The e-graph rewrite search runs and is proved sound, but it does not currently produce the fusions in a shipped packet — those are implemented in the emitter. We describe it as a working optimizer that is advisory today, not as the thing making plow fast.
  • Vision and diffusion architectures build as graphs and have fusion rules; they have no device path, so they are not served. Speech is a design, not code.
  • The Lean checkpoints prove the compiler's own output legal. They say nothing about whether a kernel computes the right function — that is a separate numerical gate against a reference.

Research context

Plow is our implementation, but the motivation is broader than one runtime. The relevant direction across the field is consistent: describe repeated work earlier, reduce request-time coordination, and keep multi-stage data on the serving side when possible.