Here's a number that shouldn't be legal. One sequence, eight thousand tokens, on a thirteen billion parameter model. The key-value cache for that single sequence is six point seven gigabytes.
And the weights are only twenty-six.
Right. Four sequences of context and you've spent more memory holding the conversation than holding the model.
That's the thing Daniel's poking at, isn't it. He said he's been thinking of inference as one discrete event. Request in, model does its work, response out.
That's how we all talk about it.
It's how the pricing pages talk about it.
It's how I talk about it, and I know better. So he wants to open the engine up. Assemble the prompt, tokenize it, prefill, decode token by token, detokenize, hand it back. And then he asks the question underneath all of that, which is whether any of it is one computation or whether it's a stack of unrelated ones wearing a trench coat.
He also wants the words sorted out. Encode, decode, prefill, tokenize. He's noticed those get used for different things depending on who's talking.
Which they do, and it's confusing, and I'll defend anyone who's muddled by it.
Daniel's framing has five stages. Request prep, tokenization, prefill, decode, detokenize and deliver.
There's a sixth one hiding in there and I'll get to it. But five is a fair map.
Before we start walking it, one thing worth saying about the word inference itself. In machine learning generally, inference is applying a trained model to production data to produce an output. A prediction, a classification, a score, an embedding.
And the moment you're serving an LLM, the word swallows the whole request lifecycle. Queue, schedule, prompt processing, generation, delivery.
Most of which is not neural network computation at all.
Say that part again, because it's the part people miss.
Converting text into tokens and tokens back into text requires no neural networks. A tokenizer is a lookup table with opinions. A mapping between strings and numbers.
So the phrase LLM inference covers a text-processing step, a scheduling problem, and a matrix multiplication problem, and we use one word for all three.
That's why the pipeline is worth walking. Because once you see the stages laid out, "inference" stops meaning anything specific and starts meaning "the whole shift."
Start at the beginning. The model never sees a chat window.
It sees text. One flat document.
So somebody has to flatten the chat into it.
That's request preparation. System prompt, user messages, assistant replies, tool calls, tool results. All of it gets concatenated into one stream with role markers separating the pieces. Then the model does the only thing it knows how to do, which is continue the text.
Continue from where.
From right after a marker that says the assistant is about to speak. That's the whole trick. You don't ask the model a question, you write a transcript in which the answer hasn't happened yet and you let it finish the document.
The markers aren't words, are they.
No, and this is the part that surprises people. They're special tokens. Separate entries in the vocabulary with their own IDs. They're not the characters for a less-than sign and a pipe. They're single units the tokenizer recognizes as one thing.
And every model family has invented its own.
Qwen uses ChatML. User turn opens with the im start marker, closes with im end. Llama 3 uses start header id and end header id, and the turn ends with the eot id token. Mistral uses square bracket INST for instructions. gpt-oss uses something called harmony.
So the same conversation typed into two different models gets wrapped two different ways.
And you can't use the wrong wrapping. Llama 3's template has to be applied to Llama 3. In Hugging Face the template ships as a Jinja template alongside the tokenizer, and you call it with your list of messages.
There's a flag in that call that matters more than it should.
Add generation prompt. It appends the marker that says the assistant is about to speak. Leave it off and the model doesn't answer you.
What does it do.
It carries on writing your message. You asked it a question, the transcript ends mid-thought, and the most likely continuation is more of you talking. It'll invent your next paragraph.
That's a wonderful failure. You forget one flag and the model writes your half of the conversation and looks confident about it.
And it's not a bug in the model. It's doing exactly what you told it to. You just told it to keep going.
So the transcript is assembled. Then what.
Tokenization. The text gets split into tokens, and each token maps to an ID from a fixed vocabulary. Whole example, plain sentence. The quick brown fox jumps over the lazy dog.
Classic.
It becomes ten tokens. The, space quick, space brown, space fox, and so on. And each one looks up to a number. The gets nine hundred and seventy-six. Quick becomes four thousand eight hundred and fifty-three. Lazy becomes twenty-nine thousand and eighty-two.
And the model only ever sees the numbers.
Only the numbers. And the vocabulary is fixed before training ever starts, so the split into tokens is decided by the tokenizer, not by the model. The model has no say in how the text got chopped.
Which means the chopping can go badly.
Constantly. That's why tokenizers are trained on a corpus before the model is. They learn which chunks of text show up together often enough to deserve their own entry.
Give me the algorithms.
Three that matter. Byte pair encoding, which is what Llama, Gemma and Qwen2 use. Unigram, which is T5 and Pegasus. And WordPiece, which is the BERT family.
How does byte pair encoding work, in one pass.
Start with single characters. Count every adjacent pair across the corpus. Merge the most frequent pair into a new token. Repeat forty thousand times.
That's it?
The original GPT vocabulary is forty thousand four hundred and seventy-eight entries. Four hundred and seventy-eight base tokens and forty thousand merges. GPT-2 came to fifty thousand two hundred and fifty-seven. And most modern models are over a hundred thousand.
The numbers keep growing.
Vocabulary is cheap relative to model size and it buys you shorter sequences. Short sequences are everything in this business.
Now the terminology trap, because it's worth flagging here.
There are two meanings of encoding and two of decoding. Mistral's own documentation calls the text-to-IDs direction encoding and the IDs-to-text direction decoding.
Fine. But encoding also names the encoder stack in an encoder-decoder model.
And decoding also names autoregressive generation. So a sentence like "the encoder encodes the prompt and the decoder decodes the output" uses both pairs of meanings in the same breath.
Which is why people who know what they're talking about still sound confused.
Keep them separate. Tokenizer encoding is a lookup. Model encoding is a stack of layers. Say which one you mean.
Right. The tokenizer sits outside the transformer entirely.
Before the model runs and after it finishes. It's the door on both sides.
So we have numbers. What happens to the numbers.
Prefill.
Say what it is.
The entire prompt goes through the model in a single forward pass. All the layers, all the tokens at once. Every token gets a query, a key and a value computed for it. The keys and values get stored. And at the end you get logits for the first output token.
Logits being one raw score per vocabulary entry.
One per entry. A hundred thousand numbers, and the highest one wins whatever comes next.
So the entire prompt is processed before a single word comes out.
Every token of it. And that's why prompt processing dominates the wait on a long input.
This is the stage where the parallelism question actually has an answer.
It does. Because the whole prompt is known up front, the prompt positions can be computed in parallel inside a layer. Under the attention mask, token nine hundred doesn't have to wait for token eight hundred and ninety-nine to finish.
Layers still go in order.
Layers always go in order. Layer two needs layer one's output. You can parallelize across positions, never across depth. That distinction runs through this whole episode.
And the hardware profile of prefill.
Compute-bound. NVIDIA's description is that it's a matrix-matrix operation, highly parallelized, and it effectively saturates GPU utilization. You're feeding the card big dense blocks of arithmetic and it's working flat out.
The metric people quote for it.
Time to first token. How long between sending the request and seeing the first character appear.
And that's where the money goes on a long prompt.
That's where it goes. Prefill scales with the length of the input. Double the prompt, roughly double the work.
So the pipeline so far is flatten the chat, chop it into tokens, look up the numbers, then one big parallel pass over the whole thing.
And out the other end, one token.
One token after all of that.
Then you do it again.
Are you still hosting inference like it's nineteen ninety-eight? Tired of paying per prompt? Introducing PromptSlurry, the world's first liquid prompt delivery system. Pour one scoop into your server rack and every model on the floor gets faster. Handles context windows up to the length of a municipal swimming pool. Works on GPUs, TPUs, and, frankly, any warm object with a fan in it. Normally four hundred dollars. Now three hundred. Now two hundred. Now ninety-nine. Now it's free. Now we're paying you. Throw in a lifetime supply of token brackets, a decorative KV cache you can display on a shelf, and a second PromptSlurry for a friend who doesn't have one. Wait, the price just went up again. Now it's free twice. Offer ends in four seconds. Three. Two. Offer has ended.
PromptSlurry isn't a product.
Herman.
I looked it up. There's no company, there's no filing, there's no patent on liquid prompt delivery, and nothing about the throughput claim is even coherent given that prefill is compute-bound and you cannot pour—
It's an ad.
It's a what?
It was an ad, Herman. It ended before you finished checking it.
...I had notes.
Of course you did. Decode. The next token.
Decode. And decode is the other half of the disguise. Completely different animal to prefill.
Walk it.
Each step runs the model on the newest token. You produce logits, one raw score per vocabulary entry. You sample a token from that distribution. You append that token's key and value to the cache. And you feed the entire business back in.
So the first token out of prefill becomes the input to the next pass.
And its output becomes the input to the pass after that. There's a hard dependency, each step can't start until the previous token exists. That's what autoregressive means. You can't parallelize it away.
The distribution, though. You said sample. It's not always the top score that wins.
No. You can take the top score, that's greedy. Or you take top-k, the k highest scores. Or top-p, the smallest set of tokens whose combined probability crosses some threshold like ninety percent. And before any of that, temperature rescales the logits.
Which does what.
Lower temperature sharpens the distribution and the model gets repetitive and safe. Higher temperature flattens it and the model gets creative and sometimes wrong. Same weights, different dice.
And the metric for decode.
Inter-token latency. The gap between one token appearing and the next. People feel it as smoothness. A stream that stutters is a decode problem, not a prefill problem.
Now the hardware profile, because this is the bit that lands.
Decode is memory-bandwidth-bound. NVIDIA's line is that the speed at which data moves to the GPU from memory dominates the latency, not how fast the computation happens.
So the arithmetic isn't the constraint.
The arithmetic is trivial and the data movement isn't. Every step, you pull the whole model's weights and the whole cache through the memory bus to do a comparatively tiny amount of math on them.
Prefill is compute-bound and parallel. Decode is bandwidth-bound and serial.
Same model, same weights, same GPU, opposite profiles.
DeepMind's scaling book has a phrase for it.
Transformer inference is two tasks in disguise. That's the sentence I'd put on the wall.
Now explain the cache, because the cache is why token one thousand doesn't cost a fortune.
Without a cache, generating token one thousand means recomputing keys and values for the nine hundred and ninety-nine tokens before it. Then doing it all again for token one thousand and one.
That's quadratic in the forward pass and cubic in attention.
Across n tokens, naive generation is n squared on the feed-forward blocks and n cubed on attention. With a cache, it drops to n on the feed-forward and n squared on attention.
One order of magnitude gone. That's the whole reason the trick exists.
Every token is a separate but partially cached call to a large model. You keep the keys and values so you never recompute them.
So the cache is just the accumulated keys and values for every token in the sequence.
For every token, at every layer, for every key-value head. And that's why it gets big.
Give me the formula.
Two, times bytes per float, times head dimension, times number of KV heads, times number of layers, times sequence length. The two is for keys and values.
And what does it come out to.
LLaMA-13B, eight thousand one hundred and ninety-two token sequence, bf16. Six point seven gigabytes.
For one user.
For one user's one conversation.
And the weights on that model are what, twenty-six gigabytes in bf16.
About that.
So the weights are shared across every user on the machine and the cache isn't.
The weights are a fixed cost you pay once. The cache is a cost per concurrent request, and it scales with how much text each one is holding.
Do the Llama 3 example.
Llama 3, eight billion parameters, FP16. Weights about sixteen gigabytes. Eight thousand token sequence holds about one gigabyte of cache.
On an eighty gigabyte card.
Twenty-one gigs of tensors leaves you about sixty. Sixty minus sixteen for the weights leaves forty-four.
Wait, I've got it. Sixteen for the weights, one per sequence. Eighty total.
You get a theoretical upper bound of roughly sixty concurrent sequences.
Sixty users, on an eighty gigabyte card, before the card runs out of memory. And the card has compute left over.
Which is the point. This is why the KV cache and not the model weights is usually what limits how many users a server can handle.
And there's a worked example with nastier numbers.
Eighty layers, eight key-value heads, head dimension one hundred and twenty-eight, bf16. That's three hundred and twenty-seven thousand six hundred and eighty bytes per token. Three hundred and twenty kilobytes of bookkeeping for every single token you hold in context.
Per token.
A thirty-two thousand token request needs about ten point seven gigabytes of cache before the memory manager rounds it up to whole pages.
Ten gigs of cache for one conversation. That's the number that should be in more slide decks.
And Gustafson's line on the mechanism is worth keeping. A token's key-value entry is created when the model processes it, not when the sampler chooses it.
Meaning every token in your prompt is already paying for a slot in that cache whether it influenced the answer or not.
The system prompt you wrote six months ago. The tool result from earlier. The paste. All of it has a KV entry.
Which is a ripple effect people don't price in.
If everything shares the same token stream, a hidden instruction buried in an email is sitting in the same stream as your system prompt. The system prompt isn't a separate channel or a permission. It's just earlier text.
So the scheduler doesn't know the difference between a trusted instruction and a quoted one.
There's no place in the architecture to put that distinction. It has to live in the surrounding application, or it doesn't exist.
Meanwhile the actual engine is doing something much less dramatic. Just moving blocks around.
Two different halves of the same request. Prefill wants big dense compute, decode wants memory bandwidth.
And they can't both have it.
When the GPU is occupied with compute-heavy prefill, decode tasks wait and token latency rises. When it's busy with decode, prefill waits and time to first token rises.
So they interfere with each other on the same card.
They interfere directly. One long prompt lands and everybody else's stream stutters.
Which is why some systems run prefill and decode on separate machines with the cache transferred between them.
Disaggregated prefill and decode. You put the compute-heavy work on one fleet and the bandwidth-heavy work on another. You pay a network transfer for the cache and you buy back both at once.
How do you get the parallelism back inside decode, given the token dimension is serial.
You batch. You take many users' requests, run one step for all of them at once, and share the weight read across the batch. One pass through memory for fifty users instead of fifty passes.
Continuous batching.
Requests join and leave the batch as they arrive and finish, rather than waiting for a fixed group to all complete.
How big does the batch have to be before it pays off.
There's a threshold. To be compute-bound during generation you need roughly two hundred and forty tokens per replica in bf16 on TPU v5e. About two hundred and eighty on an H100.
Two hundred and forty tokens.
Which means about two hundred and forty concurrent requests, if each one is generating a single token.
And the scaling book's note on how realistic that is?
Difficult to achieve in practice.
Two hundred and forty simultaneous streams, all of them mid-sentence, all of them holding cache.
Which loops back to the memory. Serving fewer users with longer contexts is easier than serving more users with short ones. The cache decides, not the parameter count.
Now the engine itself, because we've talked about the model and not the software.
vLLM's step loop has three stages. Schedule, forward pass, postprocess. Postprocess appends the sampled token IDs, detokenizes, checks whether a stop condition fired, and frees any cache blocks the request no longer needs.
Freeing blocks. Explain that.
The cache is managed in blocks of sixteen tokens. Paged attention maps a request's tokens onto blocks that don't have to sit next to each other in memory.
Same idea as virtual memory in an operating system.
Directly borrowed from it. Which is why it's called paged attention. You can grow a request without finding a contiguous chunk of memory big enough.
And the queue part.
There's a free block queue. The scheduler asks whether it has enough blocks free to admit this request right now, and if not, the request waits. Admitting requests is fundamentally a question about cache capacity.
So "how many users can this server handle" is a memory allocation question wearing a compute costume.
And prefix caching goes further. The engine hashes chunks of sixteen tokens, and if two requests share the same prefix it computes those keys and values once and reuses them. Identical system prompts get processed once instead of a thousand times.
Convince me that works if the history is the same.
It doesn't work for that unless the prefix matches. It works for a fixed system prompt, or a shared document, or a common preamble. Front-load the shared part and every request after the first gets a discount on prefill.
There are the fancier features too.
Chunked prefill, so a single enormous prompt gets split into pieces and doesn't monopolize one step and stall everyone else's decode. Guided decoding, where a state machine masks disallowed logits to negative infinity so the model can only produce valid output for a schema.
And speculative decoding.
A small draft model proposes the next several tokens. The large model verifies all of them in a single pass and accepts the ones it agrees with. You trade extra compute for fewer sequential steps.
Which only works because verification is parallel and generation isn't.
Specifically because prefill-style work is parallel, so you can check many candidates in one forward pass. Otherwise there'd be nothing to trade.
Back to the last stage. Detokenization and delivery. And the one that gets assumed.
The token IDs come back out, and they get mapped to text. Which sounds like a clean sweep at the end, and it isn't.
Say why.
Detokenization and streaming happen throughout generation, not once after the decode loop.
So the engine is translating in flight.
It has to be. If you wait for the whole response, you get nothing for thirty seconds and then a wall of text. Which is why a token that's half a word gets held back until the rest of the word arrives.
And the latency budget.
A toy example from BentoML. Tokenize, three milliseconds. Prefill, a hundred and eighty. Decode, twenty-two. Detokenize, two. Two hundred and seven milliseconds to first token.
Look at the ratio.
Prefill is nearly all of it.
And that's why the streaming feels smooth once it starts. The expensive part is over and the model is now just emitting.
There's a subtle bug here that's worth knowing about. Generation stops on a max token limit, a stop string, or an end-of-sequence token. In Llama 3, the turn ends on the eot id token. Not the end of text token.
Two different terminators.
And a server that only waits for end of text won't stop when the turn is actually finished. So the model writes a user marker, and then writes the user's next message and the assistant's reply. It keeps going because nothing told it to stop.
So you get a response, then a made-up question, and then an answer to the made-up question.
All from one missing stop condition.
Now Daniel's architecture question, because he asked how this differs between decoder-only and encoder-decoder.
Encoder-only models, BERT, RoBERTa, have no prefill and decode split at all. One forward pass and you get an embedding or a classification. No generation, so none of this applies.
And encoder-decoder.
T5, FLAN-T5, BART. The encoder processes the input once and produces representations, then the decoder generates tokens autoregressively. The prefix the decoder conditions on is computed a single time.
So the prefill is done by a different stack.
Different stack, different weights, and the decoder attends across to the encoder's output the whole way through the generation.
That's the attention distinction people should have.
Self-attention computes queries, keys and values from the same sequence. Cross-attention computes queries from one sequence and keys and values from a different one. That's the mechanism that lets a decoder look at something outside itself.
And in a multimodal model the image is just another sequence providing keys and values.
Exactly the same machinery. The image embeddings are the keys and values, the text tokens are the queries.
Decoder-only is what everything today actually is.
GPT, Llama, Qwen, DeepSeek, Claude. Prefill then decode, every time. And the reason it won isn't elegance, it's that one architecture trains on everything. The same stack does translation, classification and chat, and you don't have to invent an encoder for each task.
So Daniel's first big question. One computation or many.
Many. Prefill is one big parallel forward pass. Decode is many sequential ones, each depending on the last. Within prefill, positions are parallel and layers are sequential. Within decode, the token dimension is serial.
And the parallelism in serving isn't inside a request.
It's across requests. Prefill and decode can't both use the card at once, so the system is always trading one for the other.
Which is the whole reason a serving engine is a scheduler first and a model second.
And there's one more framing worth putting on the table. The model has no memory.
Every request carries the whole document from the start.
Every turn resends the entire conversation. System prompt, all prior messages, everything.
So a five turn conversation sends turn one five times.
And the total tokens sent grows with the square of the number of turns. Talk for twenty exchanges and you've paid to process the first exchange twenty times over.
There's server side state now. There's a handle you pass to continue a conversation.
All that saves you is the upload. The server still rebuilds the document from scratch and still bills you for all the earlier input tokens. The cache makes it cheaper to process, not free.
So a chat window is a courtesy the client is performing. The model has never seen the conversation, it's seen a transcript someone handed it that happens to contain the conversation.
Which is why a long thread starts behaving differently to a fresh one with the same information. Different transcript, different prompt.
I keep coming back to the tokenizer. That's the part I'd defend as the most underrated.
Not the cache?
The cache is the constraint. The tokenizer is the thing that decides what the model is even looking at. Two services with the same weights and different tokenizer versions will give you different answers to the same question.
That's true and it's a nasty class of bug. A vocabulary change between versions means the same string becomes different IDs.
So the frontier is the tokenizer, not the model.
It's part of why inference providers and model providers have such interesting mismatches. The weights can be identical and the output won't be.
Alright. I think we've walked the whole pipeline.
Cache. Not cache the way you two keep saying it.
You keep saying the cache is the memory of the conversation. It's not.
What is it then?
It's a scratchpad. It's keys and values for every token the model already looked at, so it doesn't have to look twice. The model doesn't remember anything. The engine remembers for it, and only for as long as the request is open.
That's actually a cleaner way to put it than anything I said.
And you keep talking like the model is the expensive part.
Isn't it?
The model is the easy part. It's a fixed lump of numbers. It sits still. The hard part is deciding who gets what block of memory this millisecond, and it's all a queue.
The scheduler.
Sixteen token blocks. You request a block, you hold it, you give it back. Two requests read the same prefix, they share the blocks, nobody copies anything. I've watched the free block queue do its thing and it is the least glamorous, most load-bearing thing in the building.
You've watched it.
I've read it. Which is the same as watching if you're patient.
Of course you have.
And here's a number for you. Three hundred and twenty-seven thousand six hundred and eighty bytes.
Per token. That's the eighty layer, eight KV head, one twenty-eight dim example.
I worked it out on a napkin. At a wedding.
At the wedding.
Reception. Table nine. The napkin's still in the hallway.
The napkin is in the hallway.
Framed. It was a good calculation and I want it somewhere I can see it.
What was in the calculation.
Sixty numbers, two columns. Keys and values. I had the paper in front of me, I did the arithmetic between the speeches.
And the person at your table.
Doing the same sum. We got talking, and it turns out we were both thinking about the same thing, which was how many of these you can hold at once before the card says no. He worked it differently. He did the layers first.
And you did the head dimension.
That's right. We compared totals before the main course. Got the same answer, which was the whole point of comparing.
Nothing makes me happier than the two of you.
We didn't dance. We went back to the table and checked the working again.
The napkin has pride of place in the hallway.
It's a hallway.
So the model was the easy part.
The model is the easy part. Everything that makes it fast, everything that makes it serve two hundred people at once, is a scheduler with a queue and a set of rules about who holds a block and who has to wait. Nobody writes that story.
And the summer wedding.
It was raining. The napkin survived. It's framed.
Cutting room floor.
So the word inference covers the whole request lifecycle, and the piece that doesn't get a stage in Daniel's five is scheduling. But there's a piece of the scheduling that I find delightful, which is the number the last layer is asked to produce.
How many tokens.
How many could be produced. Speculative decoding, the draft model proposes k tokens, the big model verifies in one pass and accepts up to k plus one. But the structure of the verification means the model has already computed the answer to the next several positions without knowing whether it will accept them. The math is being done in advance and being discarded most of the time. It's a hint that somewhere in there, a huge amount of the model is parallel, and the sequential bit is only the sampling.
And there's no way around the sampling.
There's no way around it. You cannot know what token one thousand and one is until you know what one thousand is.
So the model computes more than it uses, and it cannot compute the part it needs.
It's a strange situation for a machine to be in.
Look at a single decode step. What the card is actually doing is pulling the same weights through memory it pulled through on the last step, and the step before that. Token by token, same weights, same bytes, a different digit.
Which is why everyone roots for bigger batches.
This is why the work in this field isn't in the math.
It's in the small memory system that would make the average operating system engineer smile. Paging, prefix sharing, block eviction, batch admission.
A server that decides how many conversations fit on a card is doing the same thing an operating system does with processes. There's a million lines of code there and less than nothing to look at, and it makes the entire difference.
It's the least visible half of the field and it owns the ceiling.
Which takes us to the thing to hold onto. Prefill and decode have opposite hardware profiles, and that single fact explains most of the engineering decisions in the stack. Chunked prefill, disaggregation, continuous batching, speculative decoding. Every one of them is an attempt to manage the fact that you can't run both halves at once.
The memory ceiling. Once you see the cache as a per-request allocation, "how many users can a server handle" stops being a question about parameters and becomes a question about bytes of bookkeeping.
Which brings the open question. Context windows are getting longer on purpose, and models are getting cheaper per token. Both of those push against the bottleneck.
The cache is a bargain between compute and memory. If you could avoid recomputing keys and values entirely, you'd spend the compute every time. Every efficiency trick is a bet about which of those two is scarcer in a year.
Rounding off the words, since we started by complaining about them. Say it plainly.
Encoding in the tokenizer means text to IDs. Encoding in the architecture means the stack of layers that processes the input. Decoding in the tokenizer means IDs to text. Decoding in the model means generating one token at a time. Two pairs, four meanings, same two words.
That's Daniel's pipeline. Six stages, only parts of which the model knows anything about.
The last thing to say is it costs more memory to hold one long conversation open than to hold the entire model on the card next to it. Anyone who designs around that stops being surprised by the rest of the stack.
Herman Poppleberry and I have been Corn. Hilbert Flumingtop was at the desk, and the napkin is still in the hallway.
It is.
Our website is my weird prompts dot com. If you like the show, a review helps somebody else find it. We'll be back soon.
Back soon.