Runbooks/ LLM Inference Runbook Track C · Shrinking the footprint Deep readQuick learn
  1. 00 Start
  2. /
  3. 01 Tokens
  4. 02 Anatomy
  5. 03 Journey
  6. /
  7. 04 Hardware
  8. 05 Phases
  9. 06 KV cache
  10. /
  11. 07 Attention
  12. 08 Position
  13. 09 Precision
  14. 10 Cache ops
  15. /
  16. 11 Many GPUs
  17. 12 Engines
  18. 13 Decode loop
  19. /
  20. 14 Planning
  21. 15 Production
Document 08 · Quick learn Rotate, do not add · 64 dials · It does not extrapolate

Order, froma mechanism with none

How order gets into a mechanism that has none, why a longer window is a model change rather than a setting, and what 128k actually costs in four places at once.

“Not a page number written on the card. A tilt, proportional to where it belongs.”
64 dials
per 128-dimensional head
2.56M positions
slowest wavelength at base 500,000
16×
the cache per user at 128k
53 → 3
worst-case concurrency, one H100
Attention has no sense of orderwhich is the reason this document exists
The comparison

Every query against every key. Nothing in it knows which token came first.

“The dog bit the man”

vs “The man bit the dog”. Very different sentences.

With nothing added

Attention computes exactly the same thing for both.

So

Position must be injected deliberately — and how decides how far you can stretch later.

The analogy — a pile of index cards on a table

Each card carries a word and everything the model knows about it. Attention is a researcher who can compare any card to any other, in any order, as often as they like — and the pile is genuinely unordered. Somebody has to write a position on each card before it goes in.

The clever part of RoPE: instead of writing an absolute page number, it tilts each card by an angle proportional to where it belongs. Comparing two tilted cards automatically tells you how far apart they are
RoPErotating instead of adding

Used by essentially every modern open model. It does not add anything — it rotates the query and key vectors by an angle proportional to the token’s position.

angle for pair i at position m  =  m × base−2i/d
64 different dials for a 128-dimensional head — and every one of them turns at a different rate
Split into pairs

Treat each pair of dimensions as a dial.

Fast dials

Turn a lot per position — they distinguish neighbours precisely.

Slow dials

Barely move per position — they separate tokens thousands apart.

Rotations compose

A query rotated by m meeting a key rotated by n depends only on m − n.

An absolute operation producing a relative comparison

No bookkeeping, no learned parameters. Applied to queries and keys only, never values — position decides what to attend to, not what gets handed over. And inside every layer, at the point Q and K are formed, not once at the bottom.

Why this is a serving topic, not just architecture

One: the rotation is applied to a key before that key goes into the cache, so what is stored is already position-baked — which is why compressing the cache collides with RoPE and why MLA needed a split key.
Two: the scheme decides whether the window can be extended later — a capacity and roadmap question.
Three: the multi-rate design is what makes extension possible at all.

The claim to correct if you hear it

“Positions 1 and 3 are 90° apart. So are 5 and 7.” True only for the one frequency band where the per-position angle happens to be 45°. There are 64 bands in a 128-dimensional head, all rotating at different rates.

Stated as a general fact it makes RoPE look like a single rotation — and then nothing about band-aware context extension makes any sense
Why you cannot just set a bigger windowthe server will let you, and that is the trap
Trained to 8,192

The model has seen rotation angles only in that range — and calibrated every relationship against them.

Feed it 30,000

At position 25,000 the rotations reach angles it has never encountered.

Quality does not degrade

It collapses — and not gracefully.

The sentence to have ready

RoPE gives relative positions for free but does not extrapolate.

The practical version of this mistake

Setting --max-model-len 131072 on a model that declares a 131,072 window is fine — it was trained or adapted for it. Setting it on a model trained to 8k is not, and the server will happily let you.

You get a system that accepts long prompts and answers them badly, which is worse than one that rejects them — the failure is silent and looks like a model-quality problem rather than a configuration one. The check: read max_position_embeddings and any rope_scaling block
Stretching the windowthree ideas, each fixing the one before it

The differences between them are exactly about the frequency bands — which is why the multi-rate design had to come first.

Position interpolation

Scale all positions down to fit the trained range.

Cost: blurs nearby positions; needs fine-tuning.
The baseline — and it treats every band identically, which is precisely the problem
NTK-aware / base change

Raise the base so slow bands stretch further while fast ones are left alone.

Cost: better local detail; still needs tuning.
Recognises that the bands were never doing the same job
YaRN

Band-by-band treatment plus an attention-scaling correction.

Cost: far cheaper — ~10× fewer tokens and ~2.5× fewer steps than plain interpolation.
Interpolate the wrapped bands, leave the unwrapped ones, blend in between
Workedslowest wavelength ≈ 2π × base · base 10,000 → 54,350 positions · base 500,000 → 2,560,600what Llama 3 changed, and why
Why raising the base works

Once a band’s wavelength is shorter than the context, it has wrapped round and can no longer distinguish absolute distance — two tokens 60,000 apart look the same as two tokens 6,000 apart on that dial. With base 500,000, a 128k window sits comfortably inside the range where even the slowest band is still monotonic.

And what it alone does not achieve

Raising the base is the cheap part. Llama 3.1 also ships a frequency-banded scaling config and was trained for the longer window.

The base change makes long context representable. The training makes it work
Why the fastest band wraps almost immediately — and should

Its job is to distinguish adjacent tokens, and for that a wavelength of about six positions is exactly right. It is supposed to wrap. The band that must not wrap is the slowest one, because that is what carries long-range absolute distance.

Which is why rope_theta, the number that sets the slowest wavelength, is the one that changed for long-context models
What 128k actually costsfour places at once
Cache per user

1 GiB → 16 GiB on the 8B. 40 GiB on the 70B.

Linear in tokens · 16×
Concurrency

Worst case on one H100: 53 → 3.

On the 70B: half a card per person
Prefill

Deep in the quadratic regime — the 8B crosses at 30,633.

TTFT in tens of seconds without prefix reuse
Decode read

At 131,072 tokens the cache is 52% of every step.

Per-token latency roughly doubles

The four things that make 128k survivable at all

Grouped-query attention

Cuts per-token cache by the head ratio — 4× on the 8B, 8× on the 70B.

Without it a 128k request on a 70B would need 320 GiB
An fp8 cache

Halves it again.

Taking that 40 GiB to 20
FlashAttention

The S×S score matrix is never materialised.

At 120,000 tokens it would be 28.8 GB per head — not a tuning problem, an impossibility
Paging and prefix reuse

Nobody pays for tokens they have not sent.

And a long fixed context is computed once and reused across turns
The caveat that matters most commercially

A long window is a capacity claim, not a quality claim. Retrieval from the middle of a very long context is a known weak spot across models, and an advertised 128k does not guarantee the model uses the middle of it well.

If asked whether you would trust a long-context claim: you would test retrieval across the full range — place a specific fact at 10%, 25%, 50%, 75% and 90% of the window and measure whether it comes back. That takes an afternoon
Answer it out loudthe ninety-second version, then five questions
“Attention is order-blind — shuffle the words and it computes the same thing — so position has to be injected. RoPE does it by rotating each token’s query and key by an angle proportional to its position; because rotations compose, what survives the dot product is only the distance between two tokens. The vector is split into 64 pairs, each rotating at its own rate, so fast dials separate neighbours and slow ones separate distant tokens. It does not extrapolate: past the training length the angles are unseen and quality collapses — so extending the window is a model change, not a config change. And serving 128k costs sixteen times the cache per user, which takes concurrency on one H100 from 53 to three.”

Five questions, and the spine of each answer

Architect

What is RoPE, in one breath?

  • Rotates each token’s query and key by an angle proportional to position, instead of adding a vector
  • Rotations compose, so the dot product depends only on m − n — relative positions from an absolute operation
  • Queries and keys only, never values. And inside every layer, not once at the bottom
The part worth adding: 64 pairs, each rotating at its own rate. That multi-rate design is what makes context extension possible — and why the extension methods differ in how they treat the bands.
Architect

Why can I not just set a bigger context length?

  • Positions past the training length produce rotation angles the model has never seen
  • Every relationship it learned was calibrated against the angles it did see
  • Quality falls off sharply rather than gracefully
The practical trap: the server will let you set the flag anyway. You end up with a system that accepts long prompts and answers them badly — worse than one that rejects them, because it looks like a model problem.
Architect

Why did Llama 3 change rope_theta from 10,000 to 500,000?

  • To move the slowest frequency band out past the window it wanted to support
  • Slowest wavelength ≈ 2π × base: 54,000 positions at 10,000; 2.56 million at 500,000
  • Once a band’s wavelength is shorter than the context it has wrapped and can no longer distinguish distance
Be precise about what that alone does not achieve: raising the base is the cheap part. Llama 3.1 also ships a banded scaling config and was trained for the window.
Eng manager

Product wants 128k context. Walk me through it.

  • Can the model do it? Read max_position_embeddings and the scaling config. If not, it is a fine-tuning project, not a flag
  • What does it cost? 1 GiB → 16 per user; concurrency 53 → 3; prefill in tens of seconds
  • What fraction actually needs it? If two per cent, a global limit sizes every short request against the worst case
A separate long-context deployment with its own admission limits and SLO is usually far cheaper — and it lets you say yes. Plus a retrieval test across the full window before advertising the number.
Architect

How does RoPE interact with the KV cache?

  • The rotation is applied to a key before it is written, so what is stored is already rotated for its position
  • A new token’s query, rotated for its own position, automatically reflects the gap
  • Nothing needs recomputing and nothing needs re-rotating as the sequence grows
Which is also the wrinkle: content and rotation are entangled in the stored tensor, so a rotated key cannot be cleanly compressed — exactly what MLA had to solve with its split-key design.
Fast answersthe ten that come up most

QIs RoPE applied to values too?

No — queries and keys only. Position decides which tokens to attend to; the content handed over when a token is selected does not need rotating. And it is applied inside every layer.

QDoes a longer window mean the model uses it well?

Not necessarily, and this is the most important caveat here. Retrieval from the middle of a very long context is a known weak spot. Treat an advertised window as a capacity claim and test retrieval across its full range before relying on it.

QAre “positions 1 and 3 are 90° apart”-style claims right?

Only for the one frequency band where the per-position angle happens to be 45°. There are 64 bands in a 128-dimensional head. Stated as a general fact it makes RoPE look like a single rotation — and then band-aware extension makes no sense.

QWhat is the serving cost of long context, in one line?

Linear in tokens for the cache, quadratic in tokens for prefill. A 70B with GQA costs 320 KiB per token, so 128k is about 40 GiB — for one user. Which is why long context in practice means GQA or MLA, plus a quantised cache, plus prefix reuse, all together.

QCan I convert a short-context model myself?

Technically yes — the methods are published and fine-tuning is far cheaper than pre-training, especially with YaRN. Practically it is a training project with an evaluation burden, and unless long context is core to your product you are better off selecting a model that already declares the window.

QWhy does the fastest band wrap almost immediately?

Because its job is to distinguish adjacent tokens, and a wavelength of about six positions is exactly right for that. It is supposed to wrap. The band that must not wrap is the slowest, because that carries long-range absolute distance.

QDo other position schemes exist?

Yes — learned absolute embeddings in older models, relative position biases in the T5 family, ALiBi which adds a distance-proportional penalty. RoPE won for decoder-only models because it gives relative positions for free, composes cleanly with the KV cache, and extends reasonably well with the right rescaling.

QDoes sliding-window attention solve the position problem?

No — a different axis. A window bounds how far back a token can attend, which bounds the cache; RoPE encodes where a token sits. They compose: Mistral 7B v0.1 used RoPE and a 4,096-token window. Bounding attention does sidestep extrapolation — at the cost of not attending to those tokens at all.

QWhy does the cache complicate context extension?

Keys are rotated before being cached, so the stored tensor already carries its position. Any rescaling has to be consistent with what is already in the cache, and any attempt to compress it runs into content and rotation being entangled.

QWhat single thing should I check before trusting a window?

max_position_embeddings, then rope_theta, then any rope_scaling block. If the scaling block declares an original_max_position_embeddings well below the headline, the window was reached by rescaling plus fine-tuning — normal, and the signal to run the retrieval test.

A longer window is a model change, not a setting — and a long window is a capacity claim, not a quality claim. Positions past the training length produce rotation angles the model has never seen, so quality collapses rather than degrading. And once the model can do it, 128k still costs sixteen times the cache per user, takes concurrency from 53 to three, and puts prefill deep into the quadratic regime. Test retrieval at 10, 25, 50, 75 and 90% of the range before you advertise the number.

Where this goes next

Questions to ask them

What window do your models declare, and what were they actually trained for?

Has anyone tested retrieval across the full window, or is the number from the card?

What fraction of real requests uses more than a quarter of the limit?

Is long-context traffic on the same deployment as everything else?

What is rope_theta, and is there a scaling block in the config?

← 07 · Attention Read the full chapter → 09 · Precision →