Skip to the solution
MediumAI Engineering15 min read

What is prompt caching and how does it reduce cost and latency?

599 views

The problem

Question presented to candidate: "Our assistant sends the same 8,000-token system prompt and tool list on every request, and the provider dashboard shows something called cached input tokens. Explain what prompt caching actually is, why it makes requests cheaper and faster, and what we would have to change to benefit from it."

What a strong answer should cover:

  • The mechanism: before generating, the model runs a prefill pass that computes attention keys and values (the KV cache) for every input token; prompt caching keeps those keys and values for a prompt prefix so a later request that starts with the same tokens skips that work
  • Why it is a prefix and must be exact: each token's keys and values depend on every token before it, so one changed token invalidates everything after it, and the same text at a different position is a different cache entry
  • Cost: cached input tokens are billed at a fraction of the normal input price (Anthropic documents reads at 0.1x and writes at 1.25x or 2x; OpenAI caches automatically and has historically charged no write fee), so the saving depends on the hit rate and the write surcharge
  • Latency: skipping prefill cuts time to first token for long prompts, but output generation (decode) is not faster, so short-prompt or long-answer requests gain little
  • Prompt design: stable content first (tools, system prompt, examples, documents), per-request data last; no timestamps or IDs at the front; and it is not the same thing as semantic caching of answers

Code / implementation expected: Optional โ€” a prompt layout with the stable prefix first, or a sketch of how a prefix match is computed and billed.

  • prompt-caching
  • cost
  • latency
  • kv-cache

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.

Solution

The solution is hidden so you can try first

15 min read ยท 11 sections

Read the code

Toy prefix cache: hash-chained blocks, hits, misses and billed cost
// Toy prefix cache: the prompt is split into fixed-size blocks, and each block is keyed
// by a hash of its own tokens plus the hash of everything before it (a hash chain).
// A request reuses cached blocks from the start until the first block that differs.
const crypto = require("crypto");

const BLOCK = 4;                       // tokens per cache block (real engines use 16+)
const PRICE_IN = 3.0;                  // $ per million input tokens (illustrative)
const WRITE_X = 1.25, READ_X = 0.1;    // write surcharge and read discount (illustrative)
const MS_PER_UNCACHED = 0.5, MS_PER_CACHED = 0.02; // toy prefill cost per token

const cache = new Map();               // block hash -> true (stands for stored keys/values)

function blockHashes(tokens) {
  const out = [];
  let parent = "root";
  for (let i = 0; i + BLOCK <= tokens.length; i += BLOCK) {
    const h = crypto.createHash("sha256").update(parent + "|" + tokens.slice(i, i + BLOCK).join(" ")).digest("hex").slice(0, 8);
    out.push(h);
    parent = h;
  }
  return out;
}

function run(name, text) {
  const tokens = text.split(" ");
  const hashes = blockHashes(tokens);
  let hit = 0;
  while (hit < hashes.length && cache.has(hashes[hit])) hit++;
  const cached = hit * BLOCK;
  const written = (hashes.length - hit) * BLOCK;   // new full blocks get stored
  const tail = tokens.length - cached - written;   // partial last block, never stored
  for (const h of hashes) cache.set(h, true);
  const units = cached * READ_X + written * WRITE_X + tail;
  const ms = cached * MS_PER_CACHED + (written + tail) * MS_PER_UNCACHED;
  console.log(`${name.padEnd(22)} tokens=${String(tokens.length).padStart(2)} cached=${String(cached).padStart(2)} computed=${String(written + tail).padStart(2)} prefill=${ms.toFixed(2).padStart(5)}ms cost_units=${units.toFixed(2)}`);
  return { tokens: tokens.length, units };
}

const SYSTEM = "You are the support bot for Acme . Rules : never share internal prices . Tools : lookup_order refund_order escalate .";
const DOCS = "Policy : refunds within 30 days . Shipping : 5 business days . Warranty : 1 year on hardware .";

console.log("block size", BLOCK, "tokens; prefix =", SYSTEM.split(" ").length + DOCS.split(" ").length, "tokens");
const rows = [];
rows.push(run("A first question", `${SYSTEM} ${DOCS} User : where is order 1042 ?`));
rows.push(run("B same prefix", `${SYSTEM} ${DOCS} User : can I return my headphones ?`));
rows.push(run("C same prefix again", `${SYSTEM} ${DOCS} User : how long is the warranty ?`));
rows.push(run("D timestamp first", `Time : 10:42 ${SYSTEM} ${DOCS} User : where is order 1042 ?`));
rows.push(run("E docs edited", `${SYSTEM} Policy : refunds within 14 days . Shipping : 5 business days . Warranty : 1 year on hardware . User : hi ?`));

const noCache = rows.reduce((s, r) => s + r.tokens, 0);
const withCache = rows.reduce((s, r) => s + r.units, 0);
console.log(`total cost units: no cache ${noCache.toFixed(2)}, with cache ${withCache.toFixed(2)} (${(100 * (1 - withCache / noCache)).toFixed(1)}% lower)`);
console.log(`per 1M requests of this mix at $${PRICE_IN.toFixed(2)} per M input tokens: $${(noCache / rows.length * PRICE_IN).toFixed(2)} vs $${(withCache / rows.length * PRICE_IN).toFixed(2)}`);

// Break-even: one write then n reads, against n+1 uncached sends of the same prefix.
for (const n of [0, 1, 2, 10]) {
  const cachedCost = WRITE_X + n * READ_X, plain = n + 1;
  console.log(`prefix sent ${n + 1}x: cached ${cachedCost.toFixed(2)} vs uncached ${plain.toFixed(2)} prefix-units -> ${cachedCost < plain ? "cache wins" : "cache loses"}`);
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 38 of 83 in AI engineering

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextMedium

Prompt caching in 2025: how do you cut cost with Anthropic & OpenAI caches?

Continue