Caching Strategies for Research Compute
Storing the result of an expensive computation the first time you run it, so the second and hundredth time you ask the same question you get the answer back instantly instead of recomputing it.
Prerequisites: Anatomy of a Quant Research Environment
A researcher building a factor from ten years of daily prices for four thousand stocks runs the same feature-engineering script a dozen times a day — tweaking a parameter, rereading the output, tweaking again. If every run recomputes the raw data joins, the rolling windows, and the cross-sectional ranks from scratch, each iteration costs minutes, and minutes multiplied by a dozen iterations a day multiplied by a whole team adds up to a lot of waiting around for nothing new. A cache breaks that cycle: it stores the result of a computation the first time, keyed to the exact inputs that produced it, and hands that result straight back on every future request with the same inputs — no recomputation, no waiting.
What actually gets cached, and where
The simplest cache lives in memory for one Python session — a dictionary mapping "these inputs" to "that output" — but disappears on restart. A disk cache writes the result to a file, keyed by a hash of the inputs, so it survives restarts and can be reused tomorrow or by a teammate. A shared cache — a database or object store the whole team reads from — means the first person to compute an expensive universe-wide feature saves everyone else from repeating it.
The part that actually makes caching work, rather than silently wrong, is the cache key. A key built only from the function name and arguments will happily return a stale answer if the underlying data changed but the arguments didn't — get the key wrong and a cache turns from a speed tool into a source of quiet, hard-to-diagnose bugs where yesterday's numbers keep showing up today.
Worked example: caching a rolling volatility feature
A researcher computes 60-day rolling realized volatility for four thousand names, a step that takes 40 seconds. Naively, that 40 seconds is paid every time the notebook reruns — easily 20 times in a session, or over 13 minutes of dead time for identical output. With disk caching keyed on (universe date, window length, price data version), the first call pays the 40 seconds and writes the result to a file; every subsequent call with the same key reads the file in under a second. The saving compounds further if a teammate on the same shared cache needs the identical feature for a different project — they get the free result too, without ever running the 40-second computation themselves.
What this means in practice
Caching pays off most for computations that are expensive relative to how often their output actually changes — a universe-wide feature recomputed dozens of times a day but only genuinely different once new data lands is a perfect candidate. It pays off least for cheap, fast-changing computations, where the bookkeeping costs more than just redoing the work. The discipline that keeps a cache trustworthy is invalidation: a clear rule for when a stored result is thrown away and recomputed, tied to a data or code version, not left to "seems fine, hasn't changed lately."
A cache trades compute for storage: pay once to compute a result, then read it back cheaply on every later request with the same inputs. The value of a cache lives entirely in its key — get the key wrong and you silently serve stale results instead of speeding anything up safely.
Version your cache keys on the underlying data, not just the function arguments. A feature computed from "prices as of March" should never be served back in June once prices have been restated, even if the function call looks identical.
Related concepts
Practice in interviews
Further reading
- Kleppmann, Designing Data-Intensive Applications (ch. 3, caching)