Xiao Wang asks, Da Wang answers · An inquiry into model memory

How does one token’s
cache become
a shared memory?

From the KV cache, prefix hits and Claude’s cache breakpoints, all the way to YOCO, shared state and sparse reading. Twenty-two questions and answers put “can we store it?” and “can we afford to compute with it?” into one picture.

Xiao Wang × Da Wang22 Q&As · 17 diagrams4 offline labs2026 · 09 · 10
THE QUESTION BEHIND THE QUESTION
Where can the same history save computation?ABCDEQuestionComputed context stateKV memoryContinueFind itprefix → stateMake it smallershared global KVIn the serving systemIn the model architecture

By the end, you should be able to tell apart identity, reuse, the size of the state, and the cost of what each step reads.

Contents · 6 parts · 22 Q&As

Xiao Wang: “Compute one token, save one token, and find it again later with a hash. Why do we still need so many caching rules?”

Da Wang: “That plan can work. What follows answers four things in turn: which states get stored, how the index is organized, whether an old state can be restored, and how much history each step still has to read.”

Q
Xiao Wang · the asker

Isn’t satisfied with jargon, and keeps asking whether each step can actually be built.

A
Da Wang · the explainer

Takes the question apart layer by layer with arithmetic, code boundaries and counterexamples.

Our conversation starts from the efficiency of DeepSeek V4.1 Flash, passes through the Claude API’s cache_control, and runs into a key objection: if a prefix’s identity can be represented by a hash, why do people keep saying the cache “must contain the entire prefix”? Further on, YOCO pushes the question one level deeper: if many layers each have to keep the history, could they all share a single copy?

Input tokens / representationsCached or shared stateNew or recomputed workHash, directory or restore entry

A, B, “apples” and the other tokens in the diagrams are teaching examples; how real text is split into tokens depends on the model’s tokenizer. Simulated numbers are kept clearly apart from real API metrics.

01 / COMPUTATION · Start with computation

First: what exactly is KV?

A cache pays off by avoiding repeated computation. To see how, first separate “reading input that already exists” from “generating new content.”

01
Xiao Wang · asks

Why does judging a new model end up as a conversation about caching?

Da Wang · answers

Picture an assistant that reads code, runs a terminal and fixes errors. The first time, you give it the project files; the second time, it comes back with test results; the third time, it keeps editing. The same tool descriptions, project conventions and conversation history show up in request after request.

The intuition from simple Q&A

One input → one answer

It’s easy to look only at whether the answer is clever and how fast the words come out.

→
The reality of multi-turn tasks

History sent back again and again → many computations → an acceptance check

The cost of reprocessing the same context also affects whether a task is affordable at all.

So a useful question to ask of Flash-class models is: at the same acceptance standard, how much did the whole task cost, how long did it take, and how many retries did its failures need? Cheaper input processing can change that answer.

02
Xiao Wang · asks

What exactly does the KV cache store?

Da Wang · answers

In plain words first: after a model reads a position, it leaves behind information that later positions can query. In attention, K (Key) helps decide “which position to attend to,” V (Value) supplies the content that gets read out, and Q (Query) stands for the current lookup. All three are vectors of numbers: not the original text, not a summary of an answer, and not an ordinary database record.[1]

FIG 01Q, K, V: reading the history with a question in hand
↔ Scroll sideways to see the whole diagram
Q, K, V: reading the history with a question in handShows one attention layer and one attention head. Real models have many heads and many layers; the diagram leaves out scaling, position encoding, normalization, residual connections and other details.Existing tokens: first turned into vectors, then into each layer’s K / VIK1 / V1likeK2 / V2applesK3 / V3Current tokenQ · queryMatch K, then read V by weightK: helps decide “whom to look at”V: the information that is read and mixed
The structure of one attention layer and one attention head. Real models have many heads and many layers; the diagram leaves out scaling, position encoding, normalization, residual connections and other details. [1]
Attention(Q, K, V) = softmax(QKᵀ / √d) V

First score how well the current query matches each Key, then add up the Values by those weights.

A single call usually does two kinds of work. Prefill processes a long input that is already given, and can usually compute many positions in parallel batches. Decode, in ordinary autoregressive generation, produces the next token from what came before. The cache lets later computation use the K/V already produced, instead of encoding the old input again and again.[1]

“Every token has its own state” describes a logical correspondence. It doesn’t mean Prefill runs the input one token at a time, and it doesn’t mean every token gets its own GPU memory allocation.

03
Xiao Wang · asks

Why isn’t “same KV means a hit” precise enough?

Da Wang · answers

As a goal for reuse, the sentence is fine. But the server has to decide whether it can reuse anything before it recomputes the KV. The common approach is to give the input prefix an identity that can be looked up quickly, such as a prefix tree or a hash, and then find the matching cached state.[2]

FIG 02The lookup happens before the expensive model computation
↔ Scroll sideways to see the whole diagram
The lookup happens before the expensive model computationIf you recomputed all of the KV first and compared afterwards, you would already have paid the main cost. The match is based on the input’s identity and the relevant settings.New input prefixIdentify input + settingsCompute cache keyCheap hash / prefix lookupIs the state still there?Usable state foundLoad the KV already computedthen process only the missed partMiss: run the modelkeep a new cache by the rules
If you recomputed all of the KV first and compared afterwards, you would already have paid the main cost. The match is based on the input’s identity and the relevant settings. [2]

For a causal model with fixed weights and the same input representation, positions and attention rules, an identical prefix is a sufficient condition for exact reuse, and one that is easy to check. Real systems also have to consider the model or adapter identity, multimodal content, tenant isolation and similar conditions.[3]

A sufficient condition is not the only mathematically possible one. Different inputs can happen to produce the same intermediate values, and special models may have stronger state equivalences. But an ordinary prefix cache doesn’t need to prove every equivalence; it picks a lookup rule whose correctness is easy to guarantee.

02 / IDENTITY · Down to the token, step by step

Does the whole prefix really have to be “stored whole”?

The key distinction here is between a prefix’s identity and the state computed from it. Separate the two, and hashes, per-token caching and block caching all fit into one picture.

04
Xiao Wang · asks

Isn’t a request either a “hit” or a “miss”?

Da Wang · answers

A request can reuse just the first part of its input. Suppose a request has 110,000 input tokens in total: 100,000 of them come from an existing cache, and the remaining 10,000 have to be processed again. Counted by tokens, the hit rate is:

100,000110,000 ≈ 90.9%

This is a teaching example. A real metric must first say what its numerator and denominator are, and what it covers.

“The share of requests with any hit at all” and “the share of all input tokens that hit” are two different metrics. If each of a hundred requests hits just one token, the first can still be 100%, without necessarily saving much computation.

LAB 01Change one position and see where exact prefix reuse stops
Previous input · a usable per-token cache is already built
ABCDEFGHIJKLMNOP
This input · green can be reused; orange tokens may match, but what came before them has changed
ABXDEFGHIJKLMNOP
2Reusable prefix tokens
14To process again this time
12.5%Hit rate, counted in tokens

Only position 3 of 16 changed, yet an ordinary exact prefix cache can directly reuse only the first 2. Identical tokens after it don’t make the diverged states merge again.

Teaching simulation: assumes full-history attention, a fixed configuration and per-token lookup entries that are all in place, and ignores eviction and expiry. It shows the rule of an exact prefix cache; no real model is called.

This also explains a common piece of engineering advice: as long as it doesn’t change what the task means, put stable material as early as possible, and put changing content, such as timestamps and this turn’s question, later. One dynamic field near the start can stop a long stretch of identical text after it from being reused directly.[4]

05
Xiao Wang · asks

It’s the same “apples”. Why can’t it just use the same KV?

Da Wang · answers

Compare “I like apples” with “I hate apples”. The last token is the same and sits in the same position, but when the model processes “apples”, it reads a different history. The representations produced by earlier attention layers become the input from which later layers compute K/V.

FIG 03Same “apples”, different conditions
↔ Scroll sideways to see the whole diagram
Same “apples”, different conditionsWith the standard position-wise input transform, the K/V before the first attention layer can be identical; once earlier attention layers have mixed in different histories, the K/V in higher layers usually differ. The word “may” in the diagram matters: a different prefix does not prove that every value must differ.Fix the token and the position; compare what came beforeIlikeapplesLayer 1 K/Vcan be identicalUp, mixing in contextIhateapplesLayer 1 K/Vcan be identicalUp, mixing in contextHigher-layer “apples” may differSame word + same position identifies only the position, not the context it has been through.
With the standard position-wise input transform, the K/V before the first attention layer can be identical; once earlier attention layers have mixed in different histories, the K/V in higher layers usually differ. The word “may” in the diagram matters: a different prefix does not prove that every value must differ. [1]
KViℓ = Fℓ(x1, …, xi; model and runtime conditions)

F stands for the model’s computation. It is a different thing from the hash function used to look up the directory.

So using only token_id or (token_id, position) as a general cache key for higher-layer KV lumps different contexts together. The key also has to tell apart what came before.

But this does not yet show that “the cache key must contain the entire prefix in plain text”. That is exactly the next question.

06
Xiao Wang · asks

“B comes after A” can be represented with a hash map. Is that objection right?

Da Wang · answers

Yes. Your parent-hash scheme works. A prefix’s identity can be represented recursively. Let h₀ encode the initial identity conditions, and for each token compute:

hi = Hash(hi−1, tokeni, relevant identity information)

The key only has to stand for “this path through the history”; it doesn’t have to carry the entire prefix text again and again.

FIG 04The parent prefix’s hash is enough to help build the next cache key
↔ Scroll sideways to see the whole diagram
The parent prefix’s hash is enough to help build the next cache keyThe two “apples” belong to different branches of history. The system can store a parent hash or a pointer to the parent node, instead of repeating the entire prefix text in every key.IShared parent state h₁likeh₂ = Hash(h₁, like)hateh₂′ = Hash(h₁, hate)applesKey: Hash(h₂, apples)applesKey: Hash(h₂′, apples)Same token; different parent identityEach key can be short, yet the path taken to reach it is still told apart, recursively.
The two “apples” belong to different branches of history. The system can store a parent hash or a pointer to the parent node, instead of repeating the entire prefix text in every key. [3]

vLLM’s hash_block_tokens does exactly this: it hands the parent block’s hash, the current block’s tokens and extra identity information to the hash function together. That is the same idea as “parent_hash + current_token”, except that it usually works in blocks.[3]

KEY / IDENTITY

Whose state is this?

Hash(parent, token) is a short label for lookup. A rigorous production implementation also has to deal with collision risk and isolation.

VALUE / STATE

What does computing this history leave behind?

What the directory finds is the actual KV storage, or a reference to it. To keep running the model, the relevant numbers still have to be read.

The hash map you proposed can point to the real KV. The idea never required “replacing the entire model state with a few dozen bytes”. The key boundary is this: shortening the label is easy; letting the model keep working from only a small state needs a different computational structure.

Writing an equals sign between KV_B and Hash(A,B) confuses the two. The clear way to write it is cache[key] → KV, or a reference to a restorable state.

07
Xiao Wang · asks

Then why not create a reusable entry every time a token is computed?

Da Wang · answers

You can. SGLang’s public RadixCache code has a matching branch for page_size = 1; in the ordinary one-token-key mode, prefix matching is precise down to a single token. That is enough to show there is no general theoretical barrier to per-token indexing.[5]

A coarser granularity is usually a cost trade-off. Put a run of consecutive K/V into one block, and indexing, reference counting, transfer and eviction can all be handled as a group. In a simplified scheme that reuses only complete blocks, with a shared prefix of length p and a block size of b:

Reusable length r = b × ⌊p / b⌋
Extra recomputation caused only by block alignment = p − r < b

This bound only limits the loss caused by granularity. When a cache has expired or a state is incomplete, the loss can be far larger than b−1.

LAB 02Cache granularity: with fewer entries, how many more tokens do you compute?
999
Reused in whole blocksIdentical, yet recomputed at the boundaryNew suffix from the first divergence
992Tokens reusable in whole blocks
7Extra, due only to granularity
62Complete-block entries in the old request

In theory 999 can be reused; aligned to blocks of 16, 992 can. 7 are recomputed extra; the last 1 belongs to the new suffix that starts at the first divergence.

Teaching assumptions: the old and new requests are both 1,000 tokens long, and the whole-block cache has been kept and can be found. Only the loss from whole-block alignment is counted; it leaves out recomputing the last position’s logits, cache transfer, and the restore overhead of special hybrid architectures.

vLLM’s prefix caching documentation describes a scheme that reuses complete blocks. With b = 16, 999 identical tokens can be reused only up to 992 under that rule: 7 extra tokens are computed in exchange for fewer directory entries.[2]

But don’t go on to picture “per-token indexing” as “one GPU memory allocation per token”. The physical memory pool, the matching granularity and the way tree nodes are represented can each be designed separately, and a compressed prefix tree need not give every token its own tree node. Smaller physical pages can even reduce the unused space at the tail; the costs have to be measured on the actual system.

Xiao WangThe parent hash is already short. Is computing a few more hashes really that expensive?

Da Wang: A short key saves the space of storing the entire prefix over and over, but the chain still has to be built in order. vLLM’s hash_block_tokens takes a parent_block_hash, so a new block’s key needs its parent’s key first. For a sequence of length N that is being indexed for the first time, with block size b, there are about N/b key constructions, one after another. Changing b from 16 to 1 makes that chain about 16 times as long.[3]

Block size bComplete blocks when N = 1,000,000Most extra at one divergence boundaryAverage when boundary remainders are uniformly distributed
11,000,0000 tokens0 tokens
1662,50015 tokens7.5 tokens
6415,62563 tokens31.5 tokens
E[extra tokens] = (b − 1) / 2

This holds only when the shared prefix’s remainder within a block is roughly uniformly distributed. The strict upper bound is b − 1; b/2 is just the order of magnitude of the average.

What is being compared here is the extra tokens at one divergence boundary against the indexing work for a whole new path. That is not enough to declare that “a constant saving can never beat a linear cost”: the attention cost of one token can also grow with the length of the history, and a cache may be reused by many requests.

Two more qualifications. Hashing still has to read the current block’s tokens, so the total work of scanning the input is usually still O(N); an existing session can continue incrementally from a parent hash it already has, so new indexing mainly corresponds to ΔN. Compressed trees, batching and different hashing schemes may also be implemented differently. So the serial chain should be seen as a concrete cost of this kind of implementation, not an inevitable flaw of every per-token cache.

03 / RESTORATION · From algorithm to service

Same prefix: what else does a hit need?

The cache directory has to find it, the data has to still be there, and the state left behind has to be enough to continue from that position. This is the layer where an API’s cache breakpoints live.

08
Xiao Wang · asks

What exactly does Claude’s breakpoint “cut off”?

Da Wang · answers

Better ways to think of it are as a cache boundary or a restore point. Reading “breakpoint” as a cut-off point makes it easy to assume that whatever comes after it is no longer sent to the model.

Putting cache_control on a content block says this: the prefix from the start of the whole effective prompt up to the end of this block becomes a candidate for caching. Claude’s documentation sets the order as tools → system → messages.[4]

FIG 05A cache breakpoint is a restore point for “everything up to here”
↔ Scroll sideways to see the whole diagram
A cache breakpoint is a restore point for “everything up to here”The cache covers the complete prefix, including the marked content block. The API content blocks in the diagram and the physical blocks in which a GPU stores KV are two different kinds of object.Claude’s effective prefix order: tools → system → messagesTool definitionsSystem instructionsStable documents / historycache_controlThis turn’s question / new tool resultsReusable prefix entryFrom the start up to the markerContent after the marker still goes to the modelA breakpoint sets “how far to cache”; it doesn’t cut off the input after it.
The cache covers the complete prefix, including the marked content block. The API content blocks in the diagram and the physical blocks in which a GPU stores KV are two different kinds of object. [4]

Producing the underlying K/V doesn’t wait for this marker. The API marker decides how the service layer stores, looks up and bills; it tells you nothing about when the GPU starts having K/V.

Several cache breakpoints can match different degrees of stability: tools and system instructions almost never change, project material changes now and then, and the conversation keeps growing. When a longer entry becomes invalid, a shorter one may still work. But each of them means “from the start up to here”; none turns identical text later on into a cache segment that stands on its own, apart from what came before.

09
Xiao Wang · asks

If there’s automatic caching, do I still need to place cache breakpoints by hand?

Da Wang · answers

It depends on how your requests change. Claude’s current documentation supports a top-level cache_control, which puts the cache breakpoint on the last cacheable content block. For requests that keep appending to an old conversation, that is convenient.[4]

APPEND / add only at the end

Request 1: A + B

Request 2: A + B + C
The old complete prefix is still there. The cache breakpoint moves forward automatically and easily finds what was written last time.

REPLACE / swap the suffix each time

Request 1: A + question X

Request 2: A + question Y
If only A+X was ever stored, A by itself never became a cache entry, so the stable material may still miss.

In the second case, it is usually better to put an explicit cache breakpoint at the end of the stable material A. Automatic caching won’t find every stable segment for you and create entries for them. The current rule also looks back only so far: it checks at most 20 content-block positions around each boundary, and it looks for entries that were actually written before.[4]

Example A · Append-only conversation: top-level automatic caching
response = client.messages.create(
    model=model_id,
    max_tokens=1024,
    cache_control={"type": "ephemeral"},
    system=stable_system,
    messages=growing_history,
)
Example B · Fixed material + a different question each time: an explicit breakpoint
response = client.messages.create(
    model=model_id,
    max_tokens=1024,
    system=stable_system,
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": stable_document,
             "cache_control": {"type": "ephemeral"}},
            {"type": "text", "text": current_question},
        ],
    }],
)

The code shows the shape of the request; your application has to supply client, model_id and the content variables. The prefix also has to reach the model’s minimum cacheable length. Caches expire; the current documentation offers 5-minute and 1-hour options. For how to call it, its limits and what is supported, follow the documentation of the platform you use.

When does the clock start?

Claude’s current documentation starts the TTL when the request that writes or reads the cache begins. For example, if a response streams for 4 minutes, a default 5-minute TTL has only about 1 minute left; if you then also wait for a tool to run, you may be too late to reuse the same entry.[4]

FIG 11A response that just ended doesn’t mean the cache has a full 5 minutes left
↔ Scroll sideways to see the whole diagram
A response that just ended doesn’t mean the cache has a full 5 minutes leftThe sketch shows only this one write or read, with no other request refreshing the same cache entry in between. The TTL is timed from the start of the request that writes or reads the entry; whether it actually hits also depends on the entry, its availability and the platform’s rules.5-minute TTL: the time spent generating the response is counted too0–4 min: the response keeps streamingAbout 1 min leftRequest startsResponse endsCache expiresOnly a later read of the same entry refreshes the TTL, from that request’s start time.
The sketch shows only this one write or read, with no other request refreshing the same cache entry in between. The TTL is timed from the start of the request that writes or reads the entry; whether it actually hits also depends on the entry, its availability and the platform’s rules.[4]

No error, yet usage is all zeros. Why?

A cached prefix has to reach the model’s minimum length. Some of the thresholds in the current official documentation are below. A request below the threshold is still processed normally; it does not raise an error just because nothing was cached.[4]

ModelMinimum cacheable prefix
Claude Opus 5512 tokens
Claude Sonnet 5 / Sonnet 4.61,024 tokens
Claude Haiku 4.54,096 tokens

Recorded on September 10, 2026. The threshold applies to the length of the marked cacheable prefix, not just to whether the whole request is long enough. If cache_creation_input_tokens and cache_read_input_tokens are both 0, check this condition first; platform differences and model versions should still be checked against the corresponding documentation.

10
Xiao Wang · asks

How does DeepSeek raise its real cache hit rate?

Da Wang · answers

DeepSeek’s context caching documentation names three kinds of places where it persists state: the end of a request’s input and output, the common prefix of several requests, and fixed token intervals within long inputs or outputs. A later request can reuse a saved cache prefix unit only if it matches that unit completely.[6]

FIG 06DeepSeek: a stable prefix can be persisted on its own once it has been observed
↔ Scroll sideways to see the whole diagram
DeepSeek: a stable prefix can be persisted on its own once it has been observedThis follows the A+B, A+C, A+D scenario in DeepSeek’s official documentation. It assumes A is not yet covered by any other boundary: request 2 discovers A and writes it as a cache prefix unit of its own, and only request 3 can match it. A real service is also affected by asynchronous writes and availability.Request 1A · the same long materialBLeaves A+BRequest 2A · the same long materialCFinds and persists ARequest 3A · the same long materialDReuses A, runs only D① Request boundaries: end of input / end of output② Common prefix found: store it as its own unit③ Long sequences: entries at fixed token intervalsTogether the three rules add restore points
This follows the A+B, A+C, A+D scenario in DeepSeek’s official documentation. It assumes A is not yet covered by any other boundary: request 2 discovers A and writes it as a cache prefix unit of its own, and only request 3 can match it. A real service is also affected by asynchronous writes and availability. [6]

An abstract formula makes this clear. Let p be the length of the prefix two requests actually share, and E the set of boundaries that have been saved and can be restored. Under this complete-unit rule, the furthest position you can restore to directly is:

r = max({e ∈ E : e ≤ p} ∪ {0})

For a given input, the caching policy does not decide p; it can change E, and so change the r you can actually restore to.

Request boundaries catch the natural points to continue from; common-prefix detection adds the reuse points that actually occur; fixed intervals give long sequences extra coverage. All of them add to the “states that can be found and used again”; none makes two prefixes that were different the same.

The diagram above explains the service’s behavior. DeepSeek does not publish the data structure of its full cache directory in this API documentation, so you can’t conclude from it that the server must use a particular radix tree, hash chain or checkpoint spacing.

Xiao WangIf A has already been computed, why wait until a common prefix shows up before saving it to disk?

Da Wang: Every extra entry you keep takes index space, write bandwidth and storage space, and some models also need extra restore snapshots. So something has to decide which states deserve to hold those resources. That is a cache admission policy.

A common prefix being used again is a stronger reuse signal than “it has appeared once”. When resources are limited, keeping such prefixes first reduces how much single-use content crowds out the hot cache. DeepSeek’s documentation also notes that building the cache takes a few seconds, so there may still be a delay between finishing the computation and the persisted entry becoming usable.[6]

Expected benefit of keeping ≈ future reuses × cost saved each time − cost of writing, storing and restoring

This is a cost model for explaining the trade-off, not an admission formula that DeepSeek has published.

“More worth saving once it is used again” is related to the idea of cache-on-second-access. But DeepSeek also saves entries at request boundaries and fixed intervals, so the whole system can’t be summed up as a strict second-access cache, and you can’t claim that every prefix must show up twice before it is saved to disk.

11
Xiao Wang · asks

If A+B is already cached, why can’t we just cut off B and get A?

Da Wang · answers

This goes deeper than “too many index entries”. In a standard implementation that keeps the full history of K/V in every layer, the idea works: as long as the data is still there, just take the first A positions. Whether the corresponding entry can be found is a separate question.

But many long-context architectures deliberately throw away old local state. DeepSeek’s documentation also says explicitly that sliding window attention changes how its cache is stored and matched.[6]

FIG 07Saving a later state doesn’t guarantee you can cut back to an earlier position
↔ Scroll sideways to see the whole diagram
Saving a later state doesn’t guarantee you can cut back to an earlier positionThe top half is the ideal case with the full history of KV; the bottom half is a simplified model that keeps only the most recent window. A window, a recurrent state or a compressed running total can each mean that restoring to a point partway through needs extra snapshots or recomputation. The diagram does not claim to reproduce DeepSeek’s private service implementation.Full-history KV: if all the numbers are there, you can take the first part12345678The first 4 are still there; slice by positionRolling window (W=4 for illustration): at position 8, only the latest 4 remain12345678To restore position 4: the old window may be goneStored now: positions 5–8
The top half is the ideal case with the full history of KV; the bottom half is a simplified model that keeps only the most recent window. A window, a recurrent state or a compressed running total can each mean that restoring to a point partway through needs extra snapshots or recomputation. The diagram does not claim to reproduce DeepSeek’s private service implementation. [6][7]

So “a compressed restore state exists for A+B” does not automatically imply “the full restore state of A can still be sliced out of it”. The memory structure a model uses directly affects how fine-grained a cache system’s restore points can be.

12
Xiao Wang · asks

And how do SSDs and KV compression affect the hit rate?

Da Wang · answers

The same prefix can miss because the cache hasn’t finished writing, has been evicted or has expired, or because isolation conditions differ. DeepSeek’s current documentation describes a disk cache that is on by default, and says that building it takes time, that it works on a best-effort basis, and that it does not promise a 100% hit rate.[6]

01

The same state takes less space

This comes from designs such as KV quantization, compression and sharing; each works on a different dimension.

02

The same capacity holds more history

If everything else stays the same, less cached content gets evicted early.

03

It is more likely to still be there on the next visit

The real chance of a hit may go up; storage bandwidth and restore overhead still have to be counted.

The above is a conditional causal chain, not a measured result showing that a product’s hit rate went up. A smaller cache lets you keep more history, or lets you spend the resources on serving more concurrent requests; how they are finally allocated depends on the system’s policy.

Reusable = identity matches ∩ entry can be found ∩ data is available ∩ state is enough to restore

These conditions have to hold at the same time. They affect one another, so you can’t just treat them as independent probabilities and multiply them.

At this point the hash has done what it is good at: identifying things quickly. The next question starts to touch the model architecture: why is the state that has to be kept so large?

04 / ARCHITECTURE · Make history itself cheaper

YOCO: can many layers share one long-term memory?

A serving system answers “can past computation be found again?”. YOCO goes further and changes how many copies of long-term state the model leaves behind, and which history positions still need further computation.

13
Xiao Wang · asks

Why does a traditional KV cache grow with “length × layers”?

Da Wang · answers

In an ordinary full-history Transformer, each layer produces K/V from its own intermediate representation. Every layer has to remember the entire history, so the longer the sequence and the more layers there are, the bigger the cache.

FIG 08The same history leaves different K/V in different layers
↔ Scroll sideways to see the whole diagram
The same history leaves different K/V in different layersEach row in the diagram is one layer’s KV for the whole sequence; the numbers in the green squares are token positions. In an ordinary model, each layer’s state is different, so the layers can’t simply be merged at inference time; sharing across layers needs the model’s design and training to go along with it.Ordinary full-history attention: every layer keeps its own KV for the whole sequenceLayer 11234567…NLayer 21234567…NLayer 31234567…NLayer 41234567…NAcross: sequence length NDown: layers L
Each row in the diagram is one layer’s KV for the whole sequence; the numbers in the green squares are token positions. In an ordinary model, each layer’s state is different, so the layers can’t simply be merged at inference time; sharing across layers needs the model’s design and training to go along with it. [1][7]
MKV ≈ 2 × L × N × HKV × dhead × s

The 2 is for K and V; L is the number of layers, N the length, H_KV the number of KV heads, d_head the dimension per head, and s the bytes per number. Batching, metadata and other state are ignored.

This formula describes only a simplified shape of standard full-history K/V. It also makes it easy to see where several optimizations act: fewer KV heads, fewer dimensions per state, lower numerical precision, fewer kept positions, or fewer layers that each store their own long-term KV. YOCO mainly goes after the last one.[7]

01 · HEADS

Fewer KV heads

Many query heads share a few KV heads, as in GQA / MQA.

02 · WIDTH

Narrower state

Carry the history in smaller vectors or latent representations.

03 · PRECISION

Fewer bits

Store numbers at low precision; the quantization scales take space too.

04 · POSITIONS

Fewer positions

Merge, compress, or keep only some of the history positions.

05 · LAYERS

Fewer repeated layers

Let several layers share one source of long-term state.

06 · K / V

One carrier for K and V

If the model has the same vector serve both purposes, the ×2 in front has to be rewritten too.

The sixth item has to be achieved through architecture and training. In an ordinary model, K and V can differ, and the server cannot merge the two vectors into one and keep the original computation. Question 18 uses a sharing scheme with positional rotation to make this boundary clear.

14
Xiao Wang · asks

The “Once” in YOCO: what exactly is done only once?

Da Wang · answers

YOCO stands for You Only Cache Once. The original paper splits the model into a first half, the Self-decoder, and a second half, the Cross-decoder. The first half produces the context representation, from which the shared global K/V is generated; each layer in the second half reads that memory with its own queries.[7]

FIG 09YOCO’s “cache only once”: the second half shares a global KV
↔ Scroll sideways to see the whole diagram
YOCO’s “cache only once”: the second half shares a global KVThis diagram shows the original YOCO idea. The first half can use sliding window attention or a gated recurrent structure, and it still has bounded state of its own. The layers in the second half share one set of global K/V, but each keeps computing its own queries and representations.Token computation flows downwardMemory made here; the second half reads itInput tokens / vectorsFirst half: Self-decoderStep by step; bounded local/recurrent stateFirst half’s final representationOne shared global K/VKeeps growing with the tokensCross-decoder layer 1Cross-decoder layer 2Cross-decoder layer 3Each layer has its own Q and later stepsThe long-term K/V they read is one memoryOutput: predict the next token
This diagram shows the original YOCO idea. The first half can use sliding window attention or a gated recurrent structure, and it still has bounded state of its own. The layers in the second half share one set of global K/V, but each keeps computing its own queries and representations. [7][8]

“Once” means that only one set of global history K/V is kept, and several second-half layers reuse it. It doesn’t mean a token is computed only once in the whole model, it doesn’t mean the model runs only once across requests, and it doesn’t mean the first half has no state at all. The original paper makes that last point in a footnote.[7]

Ordinary full-history KV: O(LN)
YOCO’s window variant: O(N + LselfW)

Heads, dimensions and precision are left out to bring length and layer count to the fore. W is the local window width; the shared global KV still grows with N.

LAB 03Change only how many copies of the global KV are kept. What happens to memory?
All 40 layers keep the whole history16.384 GB
One global KV + local windows in the first 20 layers0.420 GB
0.410Shared global KV / GB
10.49Local state / MB
39.0×Total KV size ratio in this example

With the same per-token state width, the long-term memory for 100,000 tokens goes from 40 copies to 1. The local windows still keep about 10.49 MB; every number here follows from this example’s assumptions.

An assumed model shape, not a measurement of DeepSeek or Claude: 40 layers in all; the first 20 layers each keep 128 positions; K/V takes 4,096 bytes per token per layer (8 KV heads × 128 dimensions × K/V × 2 bytes). GB and MB are decimal. Only KV is counted, not model weights, activations, workspace, metadata and so on.

So YOCO does not squeeze a history of a million tokens into a fixed-length hash either. What it reduces is the repeated storage of long-term memory from layer to layer. The longer the context, the smaller the share taken by the fixed-size local windows.

The two halves still run in causal order: the current position can use only the history it is allowed to see, and each later new token passes through both halves again. Saying “the first half is like an encoder” does not mean it can read in both directions and see future tokens.[7]

15
Xiao Wang · asks

How can a shared KV also let Prefill skip some layers?

Da Wang · answers

In an ordinary Transformer, history positions must pass through the earlier layers to produce the K/V that each later layer uses. Even if you don’t need those history positions’ outputs, it’s hard to skip that computation.

YOCO changes the dependencies: the history K/V that the second-half layers need comes directly from the first half’s output. So history positions that are already given no longer have to pass through the whole second half just to build a cache for later use. In the public implementation, CrossDecoder.forward first generates and appends the shared K/V, then uses skip_cross_decoder to decide whether to keep running the second-half layers.[8]

FIG 10What YOCO saves is the second-half computation for prefix history positions
↔ Scroll sideways to see the whole diagram
What YOCO saves is the second-half computation for prefix history positionsWhen all you need is to go on generating the next token, history positions mainly serve to build the shared memory; the last input position still has to pass through the second half to produce the next token’s probabilities. After that, each new token passes through both halves. The table sketches dependencies; its areas are not drawn in proportion to FLOPs.Each column is an input position; each row is a group of computationx₁x₂x₃…xₙ₋₁xₙNewFirst half: Self-decoderRunRunRunRunRunRunRunMake / append shared KVRunRunRunRunRunRunRunSecond half: Cross-decoderSkipSkipSkipSkipSkipRunRunThese positions need no final outputFirst predictionKeeps generating
When all you need is to go on generating the next token, history positions mainly serve to build the shared memory; the last input position still has to pass through the second half to produce the next token’s probabilities. After that, each new token passes through both halves. The table sketches dependencies; its areas are not drawn in proportion to FLOPs. [8]

This design has to be understood position by position. If the prompt has N positions, getting the first new token’s probabilities still means running the second half on the last input position’s representation. What can be saved is the second-half computation for many earlier history positions.

When it saves

You only need to keep generating from a known prefix

Most history positions only build the memory. Their second-half representations are neither output nor used to generate future K/V.

When it doesn’t carry over

Training, or when every position must output scores

To compute predictions and losses at every position, the corresponding second-half computation is still needed. Whether a given interface can skip it depends on what it has to return.

Beyond that, using efficient local or recurrent structures in the first half also lowers the cost of processing long inputs. The speedups in the paper depend on the baseline model, the sequence length and the implementation; you can’t paste them straight onto another product.

05 / CASE STUDY · Turn the structure into a ledger

From YOCO to V4.1: what to store, recompute, and read?

Put cross-layer sharing, the 890 bytes, bounded replay and sparse reading each under its own accounting basis. Every step separates design conditions, mathematical derivation and runtime evidence.

V4.1 case study · basis of the sources

The V4.1-specific configuration, code behavior and report statements below follow the technical excerpt that accompanies the article; this article recomputes memory from those parameters and derives dependencies and complexity. The original config.json, model.py, kernel.py and the report’s full text were not obtained this time, so these points are not marked as independently verified source-code facts, nor treated as runtime measurements. General mechanisms and the official API rules that were checked directly have their own sources.[9]

16
Xiao Wang · asks

Where exactly is V4.1 “YOCO-like”?

Da Wang · answers

It can be spelled out as: which layers produce history, which layers share it, and which state each layer still keeps for itself. Following the configuration in the technical excerpt, the 40 layers split into two parts, 0–19 and 20–39; the global KV sources are [2, 8, 14, 20], and the first two layers keep only a sliding window.[9]

FIG 12The long-term state of 40 layers, concentrated into 4 sources
↔ Scroll sideways to see the whole diagram
The long-term state of 40 layers, concentrated into 4 sourcesParameters from the accompanying technical excerpt: [2, 8, 14, 20] are the KV sources. Layers 2–19 form three groups of six layers, each group including its source layer; layers 20–39 form one group of twenty. The diagram shows only which layers are sources and which use them; it does not replace a full execution graph.Compiled from the technical excerpt; layers are numbered from 0. Long history and local windows are drawn apart.Layers0–1No global sourcecompression ratio = —Each layer’s local sliding windowEach layer also has its own SWA windowLayers2–7Source layer 2compression ratio = 26 layers in all share this sourceEach layer also has its own SWA windowLayers8–13Source layer 8compression ratio = 26 layers in all share this sourceEach layer also has its own SWA windowLayers14–19Source layer 14compression ratio = 26 layers in all share this sourceEach layer also has its own SWA windowLayers20–39Source layer 20compression ratio = 120 layers in all share this sourceEach layer also has its own SWA windowSharing the global KV doesn’t automatically remove each layer’s local state.
Parameters from the accompanying technical excerpt: [2, 8, 14, 20] are the KV sources. Layers 2–19 form three groups of six layers, each group including its source layer; layers 20–39 form one group of twenty. The diagram shows only which layers are sources and which use them; it does not replace a full execution graph.[9]

The excerpt ties the second half’s global KV to the first half’s final representation, while each layer keeps its own local sliding-window state. That has a clear structural link to YOCO’s cross-layer memory sharing; inside the first half, layers can also share in groups. When comparing models, look at the actual dependency edges, not just the two names “Encoder / Decoder”.[7][9]

QuestionWhat these materials can showBoundaries that still need separating
Who produces the global KV?The excerpt lists 4 sources and their layer groups.A representation from the same source, the same projection and the same cache object have to be told apart.
Can Prefill skip the second half?The report, as paraphrased, describes a shortened path on the deployment side; the reference implementation, as paraphrased, still runs layer by layer.“The architecture allows it”, “the report says it saves” and “public code implements it” are different kinds of evidence.
How is 890 B/token calculated?It can be recomputed from the width, precision, scales and the four sources’ compression ratios.It measures the slope of the global KV, not all GPU memory.
Can 128 tokens restore the state?The excerpt describes Bounded Replay as an approximate state reconstruction.Approximate restoration needs quality evaluation; it can’t be claimed to be numerically equivalent.
17
Xiao Wang · asks

890 bytes/token: what exactly does it save?

Da Wang · answers

First, be clear about the unit: for each extra original input token, how much more global KV has to be kept. That is different from “how much GPU memory the whole sequence takes”. Under the given quantization and sharing conditions, each compressed position needs 356 bytes.[9]

FIG 13890 bytes: where does each part come from?
↔ Scroll sideways to see the whole diagram
890 bytes: where does each part come from?Recomputed under the conditions in the technical excerpt: main KV uses 4-bit numbers plus a 1-byte scale for every 16 dimensions; the indexer K uses 4-bit numbers plus a 1-byte scale for every 32 dimensions. The source layers’ compression ratios are 2, 2, 2 and 1. Compression-group tails, layout alignment and extra metadata are ignored.Storage per compressed position: numerical payload + quantization scalesmain KV · 512 dims256 B values + 32 B scales288 Bindexer K · 128 dims64 B values + 4 B scales68 BPer compressed position: 288 + 68 = 356 BThree sources with ratio = 23 × 356 / 2 = 534 B/tokenOne source with ratio = 11 × 356 = 356 B/token534 + 356 = 890 B/token
Recomputed under the conditions in the technical excerpt: main KV uses 4-bit numbers plus a 1-byte scale for every 16 dimensions; the indexer K uses 4-bit numbers plus a 1-byte scale for every 32 dimensions. The source layers’ compression ratios are 2, 2, 2 and 1. Compression-group tails, layout alignment and extra metadata are ignored.[9]
Mglobal(N) ≈ 356 × (N/2 + N/2 + N/2 + N)
= 890 N bytes

This is the linear term for long sequences; short sequences have to be calculated from the actual compression groups, rounding and memory layout.

The scales here are the factors needed to bring low-precision numbers back to their proper size. In FP4 each number takes 0.5 bytes, but you can’t just multiply by 0.5 and stop: the main KV’s scales add 32 bytes, and the indexer K’s scales add another 4. The report excerpt calls them E4M3 and UE8M0 scales respectively.[9]

Now the local windows: estimating with 40 layers, 128 positions per layer, 512 dimensions and an FP8 carrier at 1 byte per number, the numerical payload of SWA is:

MSWA,payload = 40 × 128 × 512 × 1
= 2,621,440 bytes ≈ 2.62 MB

This term does not depend on the long-context length N; SWA’s scales, alignment, metadata and so on are not yet counted. 1 MB = 10⁶ bytes; 2,621,440 bytes is also 2.5 MiB.

LAB 04The linear term of long history, and the fixed term of local windows
890.00global KV / MB
2.62SWA numerical payload / MB
892.62Both terms added / MB
892.62Both terms averaged / B per token

Under the conditions given in the excerpt, the global KV for 1,000,000 tokens is 890.00 MB, and the local windows’ numerical payload adds 2.62 MB. Together they average 892.62 B/token; other memory is not counted.

A conditional calculation, not a measurement of a product’s GPU memory. When N gets shorter, the local-window term does not shrink with it, so the total divided by N is higher than 890 B/token. This assumes N covers at least a full window.

What is countedWhat it includesWhat you can’t conclude from it
890 B/tokenThe four sources’ main KV, indexer K and the listed scales, weighted by compression ratio.Whole-model GPU memory, actual throughput or API prices.
About 2.62 MB/sequenceUnder the assumptions above, the numerical payload of every layer’s fixed SWA window.All local state and engineering overhead.
Size persisted to SSDThe states and snapshots actually chosen when writing to disk, and how they are encoded.The in-GPU-memory 890 can’t automatically confirm “1/8 of the previous generation”.
Full deployment resourcesAlso add weights, Engram, activations, workspace, concurrency, communication buffers and so on.A global KV under 1 GB doesn’t mean the model fits on a 1 GB GPU.
18
Xiao Wang · asks

Can K and V really be the same vector?

Da Wang · answers

K and V describe two uses: one takes part in matching, the other in summing up. A model can be designed so that the same stored vector serves both uses; then one fewer set of numbers has to be stored. The technical excerpt says V4.1’s kv_shared is used for both matching and the output multiplication, and the ledger is calculated on that condition.[9]

FIG 14One vector serves as both K and V, and position encoding also enters what is read out
↔ Scroll sideways to see the whole diagram
One vector serves as both K and V, and position encoding also enters what is read outA sketch of the idea of shared K/V with rotary position encoding. For clarity it uses the mathematical notation of a full two-dimensional rotation; a real kernel may rotate only some of the dimensions. It explains what the inverse rotation on the output does, and does not claim to reproduce the V4.1 kernel line by line.History position jContent vector cⱼStored oncemⱼ = Rⱼ cⱼAs Key: gives weightsαᵢⱼ comes from Q and mⱼAs Value: gets mixedo′ᵢ = Σ αᵢⱼ mⱼRotate the output into the query’s coordinatesoᵢ = Rᵢ⁻¹ o′ᵢ = Σ αᵢⱼ Rⱼ₋ᵢ cⱼStill contains the relative position j − i; not the same as restoring every Value to cⱼ.
A sketch of the idea of shared K/V with rotary position encoding. For clarity it uses the mathematical notation of a full two-dimensional rotation; a real kernel may rotate only some of the dimensions. It explains what the inverse rotation on the output does, and does not claim to reproduce the V4.1 kernel line by line.[9]

Rotary position encoding (RoPE) rotates part of a vector according to the token’s position, so that matching can express relative position. If the rotated vector used as the Key is also the Value, the summed output carries those rotations too. Multiplying the output for query position i by Ri−1 expresses it in the current position’s coordinates.

Ri−1 ∑j αij Rjcj
= ∑j αij Rj−icj

With the same set of RoPE frequencies, the rotation matrices satisfy Rᵢ⁻¹Rⱼ = Rⱼ₋ᵢ. This equality is a linear-algebra derivation.

Likewise, num_key_value_heads = 1 by itself only says there are few KV heads; it cannot on its own prove that K and V are the same vector. Whether they are shared requires looking further at the actual projections, the tensor references, and the inputs of the two matrix multiplications.

19
Xiao Wang · asks

If we replay only the last 128 tokens, can we restore the state exactly?

Da Wang · answers

Look at it as two questions: how to bring back the local state that wasn’t saved, and how exact the restored state needs to be. A single layer looks only at the most recent W positions, but once layers are stacked, the theoretical range of dependence usually widens. For a simple stack of L layers whose windows include the current position, the receptive field of a single output position can reach 1 + L(W−1).

FIG 15Replaying one window doesn’t necessarily rebuild the full multi-layer state
↔ Scroll sideways to see the whole diagram
Replaying one window doesn’t necessarily rebuild the full multi-layer stateh²[t] needs h¹[t−2], and h¹[t−2] in turn depends on the original positions t−4 and t−3. With only the original inputs at t−2, t−1 and t, and no extra boundary state, that dependency can’t be filled in automatically. The diagram is a deterministic counterexample about dependencies, not a measurement of V4.1’s error.A minimal counterexample: the window includes the current position, W = 3; only two layers of dependency are drawnt−4t−3t−2t−1tLayer 1h¹[t−2]Layer 1h¹[t−1]Layer 1h¹[t]Layer 2 targeth²[t]Replay only the last 3: t−2, t−1, tBut earlier inputs can still affect it
h²[t] needs h¹[t−2], and h¹[t−2] in turn depends on the original positions t−4 and t−3. With only the original inputs at t−2, t−1 and t, and no extra boundary state, that dependency can’t be filled in automatically. The diagram is a deterministic counterexample about dependencies, not a measurement of V4.1’s error.

The technical excerpt describes V4.1’s SWA Bounded Replay like this: to avoid saving every layer’s SWA state, it replays only one window to restore the state approximately. The excerpt also notes that the report admits the actual effective dependence may be shorter than the theoretical receptive field, and lists this kind of restoration as a robustness boundary that needs further characterization.[9]

Xiao WangThen does “Prefill runs only the first half of the model” need conditions too?

Da Wang: It does. YOCO’s visible reference implementation has skip_cross_decoder, which can follow the shared-KV dependencies to skip the second-half computation for many history positions. The V4.1 technical excerpt, on the other hand, separates two paths:

Reference implementation · per the excerpt

Runs layer by layer over the input positions

Transformer.forward still runs all 40 layers. This behavior shows the model’s computation; it isn’t enough to reproduce an early exit on the deployment side.

Report’s description · per the excerpt

First-half encoding + a short tail replay

Skips the second half for large stretches of history positions, keeps the path needed to produce the first output, and uses bounded replay to fill in the local state.

[8][9]
O(NL) → O(NL/2 + W · L/2)

A structural ledger of “how many layers × how many positions were run”, treating the work at each layer and position as roughly equal. It doesn’t automatically include all attention scans, sparse indexing, communication and queuing costs.

So getting from “the architecture allows a short path” to “the server is actually faster” still goes through the scheduling implementation and measurement. The last input position has to produce the probabilities of the first new token; scoring every position, or training, can’t directly use the skip that works when you only generate the next token.

20
Xiao Wang · asks

If a million tokens fit in storage, can we necessarily afford to compute with them?

Da Wang · answers

One more question decides speed: for every generated token, how much history state does it actually have to touch? Squeezing all the KV from 10 GB to 1 GB reduces the pressure on capacity; if many layers still scan the full history again and again, reading and matching stay expensive.

FIG 16Instead of searching the whole history again and again: coarse filter first, then rerank
↔ Scroll sideways to see the whole diagram
Instead of searching the whole history again and again: coarse filter first, then rerankCandidate counts and layer numbers follow the V4.1 technical excerpt provided. When the candidate pool is smaller, the actual available number is used; in the diagram 16K = 16,384. It only shows the structure by which later indexing on the decoder side is reused, not all of the model’s layers, local windows or other operators.A million positions can be stored, but each step needn’t read them all closelyglobal KV / index memoryCovers N history positionsOne global coarse filter: pick 2,048 blocks8 positions per block → up to 16,384 candidatesLater indexers: rerank only within the poolLayers in the excerpt: 24 / 28 / 32 / 36Pick the top 512 global positionsMain attention reads them closely; local windows are separateLater reranking has a fixed range; the first global filter still depends on N.
Candidate counts and layer numbers follow the V4.1 technical excerpt provided. When the candidate pool is smaller, the actual available number is used; in the diagram 16K = 16,384. It only shows the structure by which later indexing on the decoder side is reused, not all of the model’s layers, local windows or other operators.[9]

According to the technical excerpt, layer 20 first builds a candidate pool; several later indexers pick the top 512 only within that pool, and some other layers reuse the selection. That removes the repeated work of “every layer searching the whole history once”.[9]

Tdecoder-index(N) ≈ Tglobal(N) + m · Trerank(C)
C ≤ 16,384; m is the number of layers that rerank

This only expresses the structure. The specific candidate-selection algorithm, the Top-K kernel and the constants still decide the real time.

Sparse reading also trades off capability: if a relevant position doesn’t make it into the candidate pool, the later fine ranking usually can’t conjure it back. So you have to look at candidate recall, answer quality and real latency together. Shared KV solves “how many copies to store”, shared candidates solve “how many times to search”, and sparse reading solves “how much to read each time”.

21
Xiao Wang · asks

Does Engram let a hash find “knowledge” directly?

Da Wang · answers

Engram gives the model an ability to “read a learned vector when a certain local combination shows up”. The official public implementation first maps an n-gram to several hash addresses, fetches the embedding vectors, and then injects them into the backbone network through a projection, a gate controlled by the current hidden state, and local fusion.[10]

FIG 17Two kinds of “memory”: request state and a lookup table learned in training
↔ Scroll sideways to see the whole diagram
Two kinds of “memory”: request state and a lookup table learned in trainingEngram’s general mechanism follows the official demo code: NgramHashMapping, MultiHeadEmbedding, Engram.forward. The demo code has its own configuration and can’t be taken directly as V4.1’s production configuration; the diagram leaves out expanded details such as multiple heads and short convolutions.Prefix cache: keeps the state this request has already computedWhole-prefix identityIncludes the history’s conditionsCache directoryFinds entries already computedDynamic KV / stateChanges with this contextEngram: reads vectors learned in training, by local patternLast n tokensFixed-length local patternMulti-head hash lookupPicks the table entries to readTrained vector tablePart of the model parametersProjection, gating, local fusionCombined with the current hidden stateBoth use hashes: the top finds computed state, the bottom finds trained parameters.
Engram’s general mechanism follows the official demo code: NgramHashMapping, MultiHeadEmbedding, Engram.forward. The demo code has its own configuration and can’t be taken directly as V4.1’s production configuration; the diagram leaves out expanded details such as multiple heads and short convolutions.[10]

An n-gram here means a local combination of n consecutive tokens. Once the model and table size are fixed, each position reads only a limited number of table entries; the addresses depend only on local tokens, so they can be prepared before the corresponding deep-layer computation happens, which makes them suitable for prefetching. The official paper also discusses a system arrangement that keeps large tables in host memory.[11]

“Store every n-gram” also needs a qualification: the hash table has limited capacity, and it does not give every possible combination its own collision-free slot. Different combinations can land at the same address, and multi-head hashing and training together deal with that compression. Unlike an exact KV cache, which tries hard to avoid mismatched identities, here collisions are a condition the representation design itself has to face.[10]

The V4.1 technical excerpt lists 2/3/4-gram memory used at layers 1 and 14. That describes one particular assembly; the original Engram demo code has a different configuration. Whichever layers it is placed in, it does not get around the fact that “different context can mean different higher-layer KV”: once static table entries are fused with dynamic hidden states, they can still produce different results.[9][10]

So the “memory” inside a model includes at least two kinds of things: what this request computed in the past, and what the training stage learned for common patterns. One has its lifetime managed by the cache system; the other usually travels with the model weights. The two can exist side by side.

06 / PRACTICE · Back to the first question

Now, how do you apply this to your own agent?

The final goal is concrete: get tasks done correctly, at lower cost and with a more acceptable wait. A good-looking cache metric is only intermediate evidence.

22
Xiao Wang · asks

So when I actually call the API, what should I change, and what should I watch?

Da Wang · answers

First, map out the real input order of every request and mark the earliest position that changes. For append-only conversations, keep existing content in its original order as far as possible; for different questions about fixed material, separate the stable material from the dynamic suffix, and place cache boundaries according to the service’s rules.

Then look at the actual usage, and tell apart “cache reads”, “cache writes” and “regular input”. Don’t assume that fields called input_tokens at different providers are the same kind of total.

Claude · a common way to total input tokens
read  = usage.cache_read_input_tokens
write = usage.cache_creation_input_tokens
fresh = usage.input_tokens

total_input = read + write + fresh
hit_ratio = read / total_input if total_input else 0

In Claude’s documentation, this input_tokens does not include the cache-read and cache-write parts above. Server-side tools and multi-turn aggregation should also be checked against the corresponding usage structure.[4]

DeepSeek · counted by hits and misses
hit  = usage["prompt_cache_hit_tokens"]
miss = usage["prompt_cache_miss_tokens"]

hit_ratio = hit / (hit + miss) if hit + miss else 0

The field definitions come from DeepSeek’s context caching documentation. For batch statistics, use “sum of all hit tokens / sum of all input tokens”; hit percentages of requests with different lengths shouldn’t simply be averaged.[6]

What you seeCheck firstConclusions not to jump to
Lots of cache writes every turnIs the boundary placed after the changing suffix? Is the old entry beyond the lookup range?“The model isn’t caching.”
Only a short stretch hitsWhere is the first differing token? Did the tools or system content change?“The material after it is identical, so the server must have a bug.”
An identical request still missesWhether the write finished, expiry, isolation/routing, restore state, service guarantees.“Identical tokens must always hit.”
High hit rate, but still slowOutput length, waiting for tools, cache loading, the attention computation that follows, queuing.“A cache should mean the whole request needs no computation.”

Then put cost and acceptance back in the same table. As a pure input-price model that ignores the write premium, with input volume N and hit rate h:

Input cost = N × [(1 − h)pmiss + hphit]

Prices p are per token. A real bill also adds output, cache writes or storage and so on, and the input volume N itself changes too.

A more valuable final metric is total cost during the evaluation ÷ the number of tasks that pass acceptance, reported together with the success rate, the task types, and the median and slow tail of the time taken. Measure a cold cache and a warmed-up cache separately, so that different conditions aren’t blended into one good-looking average.

For schemes with approximate restoration or sparse reading, evaluation should also cover consistency before and after restoration, task failures caused by missed candidates, and how performance accumulates over long sessions. The optimization target is always to pass the same acceptance checks; the bytes and scans saved have to show up in the end as task cost and time to completion.

THE COMPLETE PICTURE

Xiao Wang’s one question finally opens into four layers

Identity: whose history is this?
Hashes and directories can tell paths apart with short keys, but behind each key there still has to be data you can actually use.

Reuse: where can we pick up the computation later?
The cache system handles entries, admission, retention and restoration; exact reuse and approximate reconstruction have to be evaluated separately.

State: how much must we keep?
Cross-layer sharing, K/V sharing, positional compression and quantization together decide how large the history is.

Reading: how much history does the next step touch?
Layered candidates, sparse attention and shared retrieval decide whether, once it fits, you can afford to read it.

Engram adds one more side path: put some learned patterns into an addressable parameter table.
How do we compute less repeated history? How do we find it again? How do we make it lighter? How do we read only the part we really need?

READ THE SOURCE

Sources, code and the basis of the calculations

Product rules were recorded on September 10, 2026. Official documentation, public code, the technical excerpt provided and this article’s own derivations are each labeled; links may change as the upstream projects update. All diagrams and interactions are there to explain mechanisms; the V4.1 conditional ledger is not a product measurement.

01
Hugging Face Transformers · Caching ↗

The basic mechanism of Q/K/V, step-by-step generation and the KV cache. This article’s explanation of how the first layer and higher layers depend on the history is derived from that computational structure.

02
vLLM · Automatic Prefix Caching design document ↗

Block caching, parent block hashes, identity information and the rule of reusing complete blocks. The actual engine version, attention backend and hybrid architectures may add further conditions.

03
vLLM · kv_cache_utils.py ↗

Public code read directly; find hash_block_tokens to see the parent block hash combined with the current tokens. The current source also has some fields related to partial block length; the article’s complete-block example is deliberately limited to a scheme that reuses only whole blocks, and does not claim that every new version behaves only this way.

04
Anthropic · Prompt caching ↗

Complete prefixes, explicit and automatic caching, the 20-content-block lookback rule, lifetimes and the usage fields. API behavior may change between versions.

05
SGLang · radix_cache.py ↗

Where to look in the source: RadixKey.match, page_aligned, RadixCache.match_prefix. There is a page_size=1 branch; matching granularity and tree-node compression can be handled separately.

06
DeepSeek · Context Caching ↗

The official API’s three kinds of persistence points, matching of complete cache units, the A+B / A+C / A+D example, best-effort behavior and the usage fields. It describes the service contract; it is not the source code of the full cache system.

07
YOCO · You Only Cache Once (NeurIPS 2024) ↗

The original architecture, the shared global KV, the first half’s bounded state, and the complexity analysis. The paper’s speedups can’t be taken directly as measurements of DeepSeek V4.1.

08
Microsoft UniLM · YOCO reference implementation ↗

Where to look in the source: SelfDecoder, CrossDecoder.forward, skip_cross_decoder. The article’s explanation of which positions Prefill can save matches its K/V generation and cross-layer read dependencies.

09
DeepSeek V4.1 technical excerpt · accompanying material

Taken from the external technical feedback this article is based on, recorded on September 10, 2026. The material paraphrases config.json, model.py, kernel.py and the technical report; the original files and a pinned version were not obtained this time. Model-specific fields and implementation descriptions are used as “conditions given in the excerpt”; the 890 bytes and the window numbers are this article’s own independent arithmetic, and the dependency and rotation formulas are conditional derivations.

Original model page: deepseek-ai/DeepSeek-V4.1-Flash ↗. Repository metadata can’t stand in for the file contents.

Show the parameters and claims of the accompanying technical excerpt

Below is a parameter list compiled from the technical feedback provided. It is not the complete config.json, and it does not claim to be an original file downloaded directly.

layers = 40
kv_source_layer_ids = [2, 8, 14, 20]
source_compress_ratios = [2, 2, 2, 1]
first_two_layers_global_kv = False

main_kv_width = 512
main_kv_bits = 4
main_scale_group = 16
main_scale_bytes = 1  # E4M3 (per the excerpt)
indexer_k_width = 128
indexer_k_bits = 4
index_scale_group = 32
index_scale_bytes = 1  # UE8M0 (per the excerpt)

sliding_window = 128
swa_payload_bytes_per_value = 1
index_source_layer_ids = [2, 8, 14, 20, 24, 28, 32, 36]
decoder_candidate_blocks = 2048
candidate_block_size = 8
decoder_top_k = 512
engram_layer_ids = [1, 14]
engram_ngram_orders = [2, 3, 4]

Behavior as paraphrased: only the source layers keep shared global state of their own; the same kv_shared is used for K and V, and the output applies an inverse RoPE; the minimal inference loop does not implement the Prefill skip described in the report; SWA Bounded Replay reconstructs approximately, based on the effective dependency range; the report’s SSD size comparison uses a persistence basis different from HBM. These paraphrases are kept separate from the numbers this article can recompute independently.

10
DeepSeek · Engram official demo code ↗

Public code read directly. Where to look: NgramHashMapping._get_ngram_hashes, MultiHeadEmbedding, Engram.forward, TransformerBlock.forward. It shows multi-head modulo addressing, the trained vector table, projection, gating and short convolution; it is a demo implementation, not V4.1’s full inference implementation.

11
Engram · Conditional Memory via Scalable Lookup ↗

The paper’s public abstract and the official repository’s description: conditional memory, fixed-size n-gram lookup, deterministic addressing and prefetching from host memory. This article does not borrow the specific multipliers from its experiments to estimate V4.1’s gains.

From one token’s cache to a shared memoryDiagrams and all four labs are built into this page