Concepts
Architecture
Thirteen packages, six of which carry the weight. This page follows one GGUF file from the disk to a token on a socket, and then follows one HTTP request through the same machinery from the other end.
The package map
| Package | What it owns |
|---|---|
cmd/llamay | The CLI, and the argv[0] trick that makes llamay-server serve with no subcommand. |
pkg/store | The model store: content-addressed blobs and manifests. A blob is only ever installed under the digest of its own finished bytes. |
pkg/ollama | Reading a local Ollama store, if there is one. A courtesy, not a dependency. |
pkg/gguf | The file format: parse, mmap, write. 23 tensor formats read; IQ1_S and IQ1_M deliberately absent, so a file carrying them is refused rather than read as garbage. |
pkg/arch | Which block shape a file describes, and what a model identifies itself as when the architecture field names only the vehicle. 17 entries reached by 39 declared names. |
pkg/quant | Block layouts, dequantisation, and the fused W4A8 kernels every one of them is tested against. |
pkg/tensor | Matrices, MatVec for decode, MatMul for prefill, and the worker pool that stays off the efficiency cores. |
pkg/tok | Byte-level BPE, SentencePiece and WordPiece, with four hand-written pre-tokenizers — hand-written because RE2 has no lookahead. |
pkg/model | Weight binding and the forward pass. State holds one conversation's scratch; Batcher decodes several at once. |
pkg/kv | The state engine: paging, the radix prefix tree, copy-on-write forks, versioned snapshots. |
pkg/sample | Samplers and the constraint interface they call into. |
pkg/constrain | JSON, JSON Schema, GBNF and lexicon constraints, compiled to byte-level pushdown automata. |
pkg/serve | The HTTP surface, the scheduler, continuous batching and the context routes. |
Loading a file
A GGUF file is memory-mapped rather than read. The weights are the vast majority of the bytes, they are read once per token in a pattern the kernel's page cache is good at, and copying six gigabytes into the heap to then read it linearly buys nothing.
llamay info -m file.gguf
reports whether any tensor in the file went unread, which is how a
half-wired architecture announces itself at load rather than as strange text
an hour later.
The forward pass
Prefill and decode are the same code. Batched prefill is an
optimisation of sequential decode, so if the two ever disagree, a benchmark of
the engine is a benchmark of two different models. This is the first invariant
llamay verify asserts, and it was written before any optimisation
was.
Two constraints shape the loop rather than the maths. There is
no allocation inside the token loop — scratch buffers are
allocated once per State — and the worker count avoids
efficiency cores, because a pool sized to the core count schedules a third of
its work onto cores a fifth as fast and then waits for them.
A request, end to end
The step that matters is where the KV context is acquired: at admission, not at submission. That is what makes the queue safe to have at all. A queued request is a channel and a few hundred bytes, so the memory ceiling is the batch width rather than the queue depth, and a burst of a thousand requests costs a thousand small structs instead of a thousand caches.
Before prefill runs, Cache.Match(tokens) walks the radix tree
and returns a context holding the longest prefix already computed. Only the
new suffix is prefilled, and the response reports
llamay_cached_prompt_tokens beside the usual usage — the number
that says whether the prefix cache is earning its memory.
How the tree works.
Why Go
Every hot loop is destined to be assembly or a GPU shader, so the language's
job is orchestration. There, Go's static single binary, its cross-compilation
and its scheduler are worth more than a marginal codegen advantage over C —
and they are what make make cross six targets from one machine.
The costs are real and planned for rather than discovered: no allocation
inside the token loop, a worker count that stays off efficiency cores, and
hand-written assembly for all eight block formats on both architectures. The
portable Go reference kernels stay in the tree and are correct — 2.8× to 7.6×
slower per dot product on an M4 — and LLAMAY_BACKEND=portable
runs the whole suite through them on a machine that has SIMD, because
building a fallback is not the same as running it.