What techniques reduce LLM latency at each stage of a request?
241 views
80 of 83
1
The problem
Question presented to candidate:
"Users say our AI assistant feels slow: about fifteen seconds before anything happens. It does intent routing, retrieval, a few tool calls and then generates an answer. How would you find where the time goes, and what would you change at each stage to make it faster?"
What a strong answer should cover:
Decompose before optimising: network and gateway, pre-model work (routing, retrieval, tools), queueing, prefill (time to first token grows with uncached prompt length) and decode (output tokens times time per output token)
Perceived versus total latency: streaming moves the first visible word to roughly TTFT without changing total time; measure both, at p95 and p99 rather than the mean
Prefill levers: prompt caching of a stable prefix, shorter and better-ranked context, fewer sequential model calls
Decode levers: fewer output tokens (concise formats, structured output, sensible max_tokens), a smaller or faster model where quality allows, speculative decoding or predicted outputs for mostly-known text
Application levers: run independent tools and retrievals in parallel, replace LLM steps with cheap code or a small model (routing, classification), precompute or cache, place compute near users
Trade-offs: every lever costs something (quality for smaller models, cache-hit discipline, complexity of parallelism), so each change is checked against an eval set, not only a stopwatch
Clarifying questions expected:
Is the problem time to first visible output, total time, or both, and at what percentile?
Which steps are sequential model calls, and which of them are required for every request?
Are we calling a hosted API or running our own inference stack?
Code / implementation expected: Optional โ a latency budget broken down by stage, or a trace showing where the time goes, is more valuable than code.
latency
ttft
performance
optimization
2
Try it yourself
Sketch your approach before you read the solution. That is what the interview tests.
Stuck? Get a hint.
A short nudge toward the approach. It will not give the answer away.
3
Solution
Target Audience: Engineers whose LLM feature works but feels slow, and who want a method for finding and removing latency rather than a list of tricks.
Difficulty: Hard
Verified with: Node 22.22.0 and Python 3.12.3 (a latency-budget model and a speculative-decoding simulation). The per-stage numbers in the budget are stated assumptions used for the arithmetic, not measurements of any provider; vendor claims (prompt caching latency, predicted outputs, speculative decoding speedups) are quoted from their sources with dates, checked October 2026.
How to read this doc: Every idea is explained in plain language first, then tagged with ๐ Interview term: so you also learn the words an interviewer expects. All program output shown was produced by actually running the examples; the Verified section quotes it in full.
1. Why This Even Matters โ A Story First
You
The solution is hidden so you can try first
16 min read ยท 12 sections
4
Read the code
Latency budget: where the milliseconds go and what each lever removes
// Speculative decoding (greedy version): a small draft model guesses k tokens,// the big model checks all k in ONE forward pass, keeps the agreeing prefix// and adds its own next token. The output is exactly what the big model alone// would have written; only the number of big-model passes changes.const target = ("the refund window is thirty days from delivery and store credit is offered " +
"after that window closes so customers should contact support with the order number").split(" ");
// the big model: greedy next token for a given prefixconstbigModel = (prefix) => target[prefix.length];
// a draft model that agrees with the big one except at some positionsfunctionmakeDraft(wrongEvery) {
return(prefix) => {
const i = prefix.length;
return i % wrongEvery === wrongEvery - 1 ? "uh" : target[i];
};
}
functionplainDecode() {
const out = [];
let bigPasses = 0;
while (out.length < target.length) { out.push(bigModel(out)); bigPasses++; }
return { out, bigPasses, draftPasses: 0 };
}
functionspeculative(draft, k) {
const out = [];
let bigPasses = 0, draftPasses = 0;
while (out.length < target.length) {
const guess = [];
for (let j = 0; j < k && out.length + guess.length < target.length; j++) {
guess.push(draft([...out, ...guess])); draftPasses++;
}
bigPasses++; // one pass scores every guessed position at oncelet accepted = 0;
while (accepted < guess.length && guess[accepted] === bigModel([...out, ...guess.slice(0, accepted)])) accepted++;
out.push(...guess.slice(0, accepted));
if (out.length < target.length) out.push(bigModel(out)); // the big model's own token, free from the same pass
}
return { out, bigPasses, draftPasses };
}
constBIG_MS = 30, DRAFT_MS = 3; // assumed cost of one forward passconstreport = (name, r) => {
const ms = r.bigPasses * BIG_MS + r.draftPasses * DRAFT_MS;
const same = r.out.join(" ") === target.join(" ") ? "identical" : "DIFFERENT";
console.log(` ${name.padEnd(28)} big passes ${String(r.bigPasses).padStart(2)} draft passes ${String(r.draftPasses).padStart(2)} est. ${String(ms).padStart(4)} ms output ${same}`);
};
console.log(`${target.length} tokens to generate; assume ${BIG_MS} ms per big pass, ${DRAFT_MS} ms per draft pass`);
report("plain decoding", plainDecode());
report("draft wrong 1 in 8, k=4", speculative(makeDraft(8), 4));
report("draft wrong 1 in 8, k=8", speculative(makeDraft(8), 8));
report("draft wrong 1 in 3, k=4", speculative(makeDraft(3), 4));
report("draft always wrong, k=4", speculative(makeDraft(1), 4));
5
Discussion
Other approaches, follow-ups and how it went in real interviews.