skip to content

When does memoizing a pricing calculation stop paying for the memory it consumes?

level: middleimportance: should knowfreq 50%

answer

  1. caching only pays when keys repeat
  2. count distinct keys, not calls
  3. entries times bytes per entry
  4. compare against the cost of recomputing
  5. a one-pass sweep never reuses anything

basics

~20 s

Memoization pays only when the same key is requested repeatedly. Its memory is distinct keys reached times bytes per entry, so a wide key space visited once each — every product-and-region pair in a nightly sweep — costs everything and saves nothing.

solid answer

~50 s

A memo table trades memory for repeated work, and the trade only pays when work actually repeats. Size the table as *distinct keys reached* × *bytes per entry*, not as "one entry per call": a pricing routine keyed by product and region is bounded by products × regions, which for a large catalogue is tens of millions of entries and hundreds of megabytes long before anyone notices. Then estimate the hit rate. If most keys are touched once — a nightly repricing sweep that visits every pair exactly once — every entry is written, never read, and you have paid full memory for zero saved recomputation. Memoization is compelling when the key space is small relative to the call volume, when recomputation is genuinely expensive, and when entries stay valid long enough to be reused. Otherwise recompute, or bound the table and accept misses.

go deeper

for a junior

Know that memoization buys time by spending memory, and that it helps only when the same input is computed more than once. Being able to say what the stored key is comes before any sizing discussion.

for a middle

Do the arithmetic: distinct keys times a realistic bytes-per-entry, weighed against repeat rate times recomputation cost. Explain why a single pass over every key gains nothing from a memo.

for a senior

Demonstrate that you would bound the table and reason about staleness. An unbounded memo in a long-running process is growth driven by traffic, and stale entries are a correctness bug, not a memory one.

for a principal

Own the policy question: how much resident memory per instance the team is willing to spend on caches at all, which workloads earn it, and how the hit rate gets measured so the decision can be revisited with data.

## The trade, stated as two numbers Memoization is the purest time-space tradeoff there is: keep the result of a computation so the next request for the same input is a lookup instead of a recomputation. Whether it is worth doing reduces to comparing two quantities: - **What it costs:** `distinct keys reached × bytes per entry`, held for as long as the table lives. - **What it buys:** `(calls − distinct keys) × cost of one recomputation`, minus the lookup overhead paid on every call. Both halves get estimated badly in interviews, in opposite directions. ## Costing the table: distinct keys, not calls The table's size is bounded by the number of *distinct* keys the run actually touches — never by the number of calls, and only loosely by the theoretical key space. For a pricing calculator keyed by `(product, region)`, the ceiling is catalogue size × region count. A modest-sounding 50,000 products across 200 regions is 10 million pairs; at roughly 50 bytes per stored entry (the two key fields, the price, and the per-entry overhead any keyed structure carries) that is around half a gigabyte for a cache nobody put on a capacity plan. Two observations follow. First, the *reachable* key count is what matters, and it is often far below the theoretical product. If ninety percent of revenue comes from two thousand products in six regions, the working set is twelve thousand entries and the table is trivial — but only if entries for the long tail are never created, or are evicted when they are. Second, bytes per entry is not the size of the value. A keyed lookup structure stores the key too, plus per-slot bookkeeping, plus empty slots kept deliberately so lookups stay fast. Estimating a memo as "8 bytes per price" is off by roughly an order of magnitude, and that error is exactly the size of the gap between "fits comfortably" and "does not fit". ## Costing the benefit: the hit rate is the whole story A memo entry that is written once and never read is pure loss — memory spent, plus the write, plus the lookup miss that preceded it. So the question to ask about any memo is: *what makes a key repeat?* - A **sweep** that visits every key exactly once (repricing the whole catalogue overnight) has a hit rate near zero. Memoizing it is strictly worse than not. - A **skewed live workload** where a small set of popular pairs is requested constantly has a hit rate near one, and the memo is close to free money. - A **recursive decomposition** where subproblems genuinely overlap is the classic win, and there the memo does not merely save time — it collapses an exponential number of repeated subproblems into a polynomial number of distinct ones. When memoization changes the complexity class like that, it stops being an optimization you weigh and becomes the algorithm. That last case is the important asymmetry. If the memo turns exponential into polynomial, you keep it even when the table is large. If it merely turns 100 calls into 10 calls of something that takes a microsecond, the table has to be nearly free to be worth its memory. ## Bounding, invalidating, and the honest failure modes An unbounded memo inside a long-lived process is a memory leak with a friendly name: it only grows, its growth is driven by traffic patterns nobody controls, and it fails at the worst moment — under peak load, when the key space widens. Two disciplines contain it. **Bound the table** to a fixed entry count and let it discard entries under a replacement policy, accepting a lower hit rate for a hard memory ceiling. And **decide when an entry is wrong**, because a memo assumes the underlying computation is a pure function of its key; a price that depends on a promotions table that changed at noon has a correctness problem, not a memory one, and stale-entry bugs are far more expensive than the recomputation the memo saved. A useful diagnostic when someone proposes a memo: ask for the expected number of distinct keys per hour and the expected number of calls per hour. If those two numbers are close, the memo will not pay. If the first is small and the second is large, it will. If nobody can estimate either, the right first step is to measure the recomputation cost and the key distribution, not to ship the table. ## The interview-shaped summary "Memory is distinct keys times bytes per entry — for this key space that's roughly N entries and X hundred megabytes. The benefit is the repeat rate; in a one-pass sweep it is zero, in a skewed live workload it is high. I'd memoize in the second case with a bounded table, and skip it in the first."

  • How would you estimate a memo table's footprint before writing any of it?
    Multiply the number of distinct keys the workload will actually reach by a realistic bytes-per-entry figure that includes the stored key, the value, per-entry bookkeeping and the empty slots a lookup structure keeps for speed — commonly several times the size of the value alone. Then compare that against the memory the process is allowed. Doing this arithmetic out loud is what separates a considered cache from a hopeful one.
  • Is there a case where you keep a large memo table even though the memory hurts?
    Yes — when memoization changes the complexity class rather than shaving a constant. If a recursive decomposition revisits the same subproblems an exponential number of times, the table collapses that to one entry per distinct subproblem and the alternative is not a slower run but no run at all. There the table is the algorithm, and the discussion moves from whether to keep it to how to bound the state it is keyed on.
  • What goes wrong with an unbounded memo in a long-lived service?
    It grows with traffic and never shrinks, so it behaves like a leak whose rate is set by the key distribution rather than by anything in the code. It typically survives testing, where the key space is narrow, and fails under peak load when the space widens. Bounding the entry count with a replacement policy turns an unpredictable growth curve into a fixed ceiling and a measurable hit rate.

Keeping a memo table is renting a warehouse for parts you might need again — worth it if you reorder the same parts constantly, pure cost if every order is a one-off.

saying these in an interview costs you the question

  • Sizes the memo by number of calls instead of distinct keys
  • Assumes caching is always a net win
  • Estimates bytes per entry as just the value size
  • Ignores that a one-pass sweep has no repeats to exploit
  • Leaves the table unbounded in a long-lived process
  • Never asks whether a stored result can go stale

context