Technology · Discipline

KV-cache engineering

The key-value cache is the largest thing on the card after the weights. I moved it to the host in ActiveKV, kept it hot in the Inference Fabric's broker, and in both cases measured the bus.

ActiveKV · Inference FabricPinned host memory, staged layersIntel Arc and NVIDIA GPUs
Host · pinned memory owns the history
GPU · a bounded working set
layer n−1sealed
layer nlease · owner · step · gen
layer n+1sealed
  • 3.3%worst error of the bus latency model against measurement, 32K to 262K tokens
  • 262Ktoken context on one card, at a 22 GiB device peak
  • −6.8%throughput from KV offload on the NVIDIA serving path

Costing the bus before changing anything

ActiveKV: stock serving keeps a request's whole key-value cache on the GPU, so a 32 GB card caps how much context it can hold. I made pinned host memory the owner of the cache and gave the GPU a bounded working set. The exact path re-reads every byte of history for every generated token, so I wrote the cost down as a latency model before changing anything.

The model's 64 layers · 16 hold attention history
Per layer, per token4,096 byteskey and value
Per token of history64 KiB16 layers × 4,096 bytes
Per generated token at 128K8.4 GBall of it, re-read
The copy engineabout 26 GB/sthe link every byte crosses
Copied bytes over the link rate, plus measured attention and the compute between layers.

From then on, every design choice answered to that arithmetic. I treat the cache as a memory-hierarchy problem with a measurable cost on the bus.

I count bytes before I optimise.

Staging layers under a lease

Each staged layer holds a lease. History copies run on their own queue, attention waits on the copy's event on the device, and only the rows that changed go back to the host, on a second queue, after attention signals.

Two layers through the stage · each under a lease
Copy queue
in · nin · n+1
Attention
layer nlayer n+1
Write-back queue
changed rows

Each lease names the owner, the layer, the step, a stage generation that only increases and the host pages' generation. An unknown fence outcome quarantines the transport and keeps every buffer until a drain proves every copy has finished.

Layer n+1's copy waits on layer n's readers.

A new layer cannot start until the previous stage is sealed. A buffer is reused only after a finished wait on every copy and kernel that touched it, and whole-layer staging matches fully resident attention bit for bit. Exactness came first and speed was a separate programme.

Exact recent pages, compact older pages

For long histories, the planner cuts each request into tiles and into runs of two representations. Background threads convert a page only after it is fully written in every layer, compute a CRC and publish it. Until then the page is served exact, so decode never waits on conversion and a slow conversion only costs bytes.

One long request · 32K-token tiles of 1,024-token pages
Exact BF16: the attention-sink page and the newest window
8-bit pages
The tail being written, exact

Each tile's attention returns its normalised output and its log-sum-exp. An FP32 merge kernel combines them exactly: each partial is weighted by the exponential of its log-sum-exp minus the largest one, the weighted outputs are summed and divided by the summed weights, and the merged log-sum-exp is the maximum plus the log of that sum. That is softmax over the union of the keys; I never average separately normalised outputs. I patched a log-sum-exp output into the vendor's paged-decode kernel to make it possible.

What the hybrid tier buys

Once history is off the card, the bus is the constraint, so I judge a representation by the bytes per token it stops moving; a slower kernel is acceptable when it removes enough transfer. On one Intel Arc Pro GPU serving a 4-bit-weight build of a 27-billion-parameter hybrid recurrent-attention open-weight model, the two modes compare like this, beside one change that spent reclaimed memory on purpose:

128K-token single request · per token
Exact335 msHybrid190 ms
About 8 GB of freed memory spent · 31K tokens, four requests
Before274 msAfter141 ms
Inter-token latency.

Device budgets are explicit, admission does the arithmetic before it lends a page and logs it with its formula, and each context can be checkpointed. Host-memory KV streaming has public precedent and log-sum-exp merging is known mathematics; the work is a native, lifecycle-safe integration of both on a single card with eager execution, and at long contexts with a single request stock vLLM is still faster per token than either tier. The full account is in Context beyond VRAM and Judge a representation by the bytes it stops moving.

Keeping prefixes hot from the broker

Inference Fabric: for throughput serving, I saw the cache problem from the other side. The engine's prefix cache only pays off if related work arrives together, so I made the token-aware broker build shape-pure batches and send related work to the same engine. With a 32 GB card and a 16 GB card serving one backlog:

One backlog, two cards
32 GB card
one shape at a time
16 GB card
same contract

Related work goes to the same engine; a guard refuses to mix shapes inside one batch.

What the engine's cache saw
Cache hit rate99.9%Padding waste0.42%Shape switches0

On that NVIDIA throughput path, offloading the cache to host memory measured slower, so I dropped offload there. See A message broker that understands tokens and From 44K to 163K tokens per second.

A paged cache beside a larger model

Capabilities model: the purpose-built CUDA decoder keeps its key-value cache in paged memory and admits requests against measured memory, so it can run beside a larger model on the same card.

One card's memory · shared
The larger modelRouter decoder, paged KVHeadroom
Proportions are illustrative.

See A router beside a production model.