Chris Collins

Tools · distributed

Cache simulator

Every major eviction policy — LRU, LFU, FIFO, CLOCK, ARC, 2Q, W-TinyLFU, S3-FIFO — against Belady's optimum on Zipf, loop, scan and shifting workloads or your own trace; hit-ratio curves for all of them at once, a step-through of each policy's internal state, write policies, cache stampede fixes (singleflight, jitter, XFetch, stale-while-revalidate) and hot-key and two-tier maths.

Added

Works with

How to use it

A cache workbench that runs entirely in this page. It answers the questions that decide how a cache behaves in production: how much a bigger cache buys, which eviction policy suits a workload, what each policy does on every request, what a write policy costs, what happens when a popular key expires under load, and where a hot key overloads one shard.

How to use it

  • Hit-ratio curves. Pick a workload, the policies, and the largest cache (as a percentage of the distinct keys). Every policy is simulated at a dozen log-spaced cache sizes in a Web Worker, and the curves are drawn together with Belady’s OPT (dashed) as the ceiling. For Zipf workloads two dotted reference lines are added: Che’s approximation of LRU, and the best static cache (the c most popular keys), which bounds any policy in the long run under independent requests. Hover or tap the chart for values; Table lists them all (the best online policy per size in bold). The tiles above show how many keys are seen only once (no cache can hit those) and the largest hit ratio possible (1 − distinct ÷ requests).

    • Zipf(α): independent requests, the key of rank r chosen with probability ∝ 1/rα.

    • Loop: the same L keys in order, over and over. LRU and FIFO hit nothing until the whole loop fits, the classic case where recency is exactly wrong.

    • Zipf with scans: every so often a one-off sequential scan of fresh keys (a batch job, a crawler) floods the cache. Scan-resistant policies keep the popular keys.

    • Shifting hot set: Zipf whose popular keys move every phase. Frequency-based policies without aging cling to yesterday’s favourites.

    • Zipf + loop: half the requests from each, as in the ARC paper’s mixed workloads.

    • Uploaded trace: one key per line, or CSV/TSV with a timestamp column (rows are sorted by it; the key is the next column unless you choose one). The trace stays in the page; it is never uploaded anywhere and never goes into a share link.

  • Step through. Type a short trace (keys separated by spaces) and pick two policies and capacities to run side by side. Step sends one request; each pane explains what the policy just did (“candidate d (freq 1) does not beat victim a (freq 3) — d rejected”) and draws its internal structures: LRU order, FIFO queue, CLOCK frames with reference bits and the hand, LFU priorities, ARC’s T1/T2 lists, ghost lists B1/B2 and target p, 2Q’s A1in/A1out/Am, W-TinyLFU’s window, probation and protected segments with sketch frequencies, S3-FIFO’s small, main and ghost queues, and OPT’s next-use times. The strip of requests shows each pane’s hits (green) and misses (red); tap one to jump there. ← and → step, Space plays. Presets include Belady’s anomaly.

  • Write policies. An LRU cache under a Zipf read/write mix with write-through, write-back and write-around: read hit ratio, read and write latency, how many writes reach the store, how many writes write-back coalesces into each store write, and how much acknowledged data sits dirty in the cache (lost if the node dies).

  • Stampede. Keys filled together, Poisson traffic, a TTL and a recompute time δ; a database that slows once too many queries run at once. Compare no protection, request coalescing per server (singleflight), a global lock, probabilistic early expiration (XFetch) and stale-while-revalidate, each with TTL jitter. Charts show queries started per second and queries in flight; the table runs every strategy on the same traffic.

  • Hot keys & tiers. An in-process L1 in every app server in front of a sharded Redis L2 and a database: hit ratio per tier, mean latency, requests reaching Redis and the database, and the load on every shard — where the hottest key lands, whether it exceeds a shard’s capacity, and how an L1 or a few copies of the key fix it.

⛶ Full screen (or F) gives the tool the whole window — useful for the step-through on a big screen; Esc leaves.

The policies

Policy What it does

LRU

Evict the least recently used key. One list; a hit moves the key to the front. Has the stack (inclusion) property: the contents of a cache of size c are always a subset of size c + 1, so its hit ratio never falls as the cache grows, and one pass over a trace (Mattson’s stack distances) gives the whole curve.

LFU (aging)

LFU with dynamic aging (LFU-DA): each key’s priority is L + its hit count, where L is the priority of the last evicted key, so old popularity is gradually forgotten. Evicts the lowest priority (oldest on a tie).

FIFO

Evict the oldest insertion; hits change nothing. Can suffer Belady’s anomaly — more frames, more misses.

CLOCK

Second chance: frames in a ring, one reference bit each. A hit sets the bit; on a miss the hand clears set bits until it finds a clear one and replaces it. An LRU approximation with no list updates on hits.

ARC

Two LRU lists, T1 (keys seen once recently) and T2 (seen at least twice), plus ghost lists B1 and B2 of recently evicted keys. A hit in a ghost list means that side was given too little room; ARC shifts its target size p for T1 accordingly. Self-tuning between recency and frequency, scan resistant.

2Q

First-time keys go to a small FIFO A1in (25% of the cache). Keys evicted from it are remembered (keys only) in A1out (50%); a key requested again while in A1out goes to the main LRU, Am. Correlated re-references inside A1in do not promote a key.

W-TinyLFU

Caffeine’s policy: a 1% LRU window in front of a segmented LRU (probation 20%, protected 80%). A key leaving the window competes with the probation victim and is admitted only if a Count-Min sketch of 4-bit counters (halved every 10 × capacity increments) says it is requested more often.

S3-FIFO

Three FIFO queues: a small S (10%) that lets one-hit wonders leave quickly, a main M with 2-bit frequency counters and lazy reinsertion, and a ghost G (keys only) of keys evicted from S. A key re-requested while in S moves to M; a key found in G goes straight to M.

OPT

Belady’s MIN: evict the key that will be needed farthest in the future (or, with bypass, don’t admit a key needed later than everything cached). Needs the future, so it can’t be built — it is the bound.

What to look for

  • On Zipf traffic the modern policies (ARC, W-TinyLFU, S3-FIFO) recover a large part of the gap between LRU and OPT. At small cache sizes their hit ratio can be two or three times LRU’s; at larger sizes a few points of hit ratio are still a large cut in the miss ratio — the load on whatever is behind the cache.

  • Che’s approximation tracks simulated LRU to within a fraction of a percent on Zipf traffic: a quick way to size an LRU cache without simulating.

  • On the loop, LRU’s curve is a cliff (so are ARC’s, LFU’s and S3-FIFO’s), while W-TinyLFU’s admission filter keeps part of the loop and follows OPT closely; on scans, LRU and CLOCK drop while ARC, 2Q, W-TinyLFU and S3-FIFO hold up; on the shifting hot set, frequency without enough aging lags.

  • In the stampede, every expiry of an unprotected hot key sends about λδ requests to the database at once; once that exceeds the database’s concurrency the queries slow down, more requests pile in and the herd snowballs. Coalescing cuts it to one query per key per server (or one in all), XFetch to about one early recompute, stale-while-revalidate to one background refresh with no waiting at all. Mass expiry of many keys filled together is fixed by TTL jitter.

Limits

  • All objects have the same size, so hit ratio equals byte hit ratio. Size-aware policies (GDSF, AdaptSize, LHD) and Caffeine’s adaptive window sizing (hill climbing) and its random admission guard against hash flooding are not modelled.

  • OPT here is the offline optimum with bypass for unit-size objects. With variable sizes OPT is NP-hard and only bounds exist.

  • The Zipf generator draws independent requests (the independent reference model). Real traces have temporal locality, bursts and correlation — upload one to see its real curve.

  • The write-policy model is one LRU cache with no replication; write-backs happen off the request path. The stampede model is a simplified event simulation (one recompute time with ±20% noise, database slowdown linear beyond its limit). The tiers tab uses Che’s approximation, which assumes independent requests and LRU in every tier.

  • Up to 2 million requests per workload; very large uploaded traces may take a while in the worker.

Sources

  • L. A. Belady, “A study of replacement algorithms for a virtual-storage computer”, IBM Systems Journal 5(2), 1966 (OPT/MIN); L. A. Belady, R. A. Nelson, G. S. Shedler, “An anomaly in space-time characteristics of certain programs running in a paging machine”, CACM 12(6), 1969.

  • R. L. Mattson, J. Gecsei, D. R. Slutz, I. L. Traiger, “Evaluation techniques for storage hierarchies”, IBM Systems Journal 9(2), 1970 (stack distances).

  • N. Megiddo, D. S. Modha, “ARC: A Self-Tuning, Low Overhead Replacement Cache”, FAST 2003 (the algorithm in Figure 4 is implemented line for line).

  • T. Johnson, D. Shasha, “2Q: A Low Overhead High Performance Buffer Management Replacement Algorithm”, VLDB 1994 (the full version with Kin = 25%, Kout = 50%).

  • G. Einziger, R. Friedman, B. Manes, “TinyLFU: A Highly Efficient Cache Admission Policy”, ACM Transactions on Storage 13(4), 2017;

    1. Manes, Caffeine: Efficiency and its design notes.

  • J. Yang, Y. Zhang, Z. Qiu, Y. Yue, K. V. Rashmi, “FIFO queues are all you need for cache eviction”, SOSP 2023 (S3-FIFO).

  • M. Arlitt, L. Cherkasova, J. Dilley, R. Friedrich, T. Jin, “Evaluating content management techniques for Web proxy caches”, ACM SIGMETRICS PER 27(4), 2000 (LFU-DA).

  • F. J. Corbató, “A paging experiment with the Multics system”, MIT Project MAC, 1968 (CLOCK).

  • H. Che, Y. Tung, Z. Wang, “Hierarchical Web caching systems: modeling, design and experimental results”, IEEE JSAC 20(7), 2002; C. Fricker, P. Robert, J. Roberts, “A versatile and accurate approximation for LRU cache performance”, ITC 2012.

  • A. Vattani, F. Chierichetti, K. Lowenstein, “Optimal Probabilistic Cache Stampede Prevention”, PVLDB 8(8), 2015 (XFetch).

  • M. Nottingham, RFC 5861: HTTP Cache-Control Extensions for Stale Content (stale-while-revalidate).

  • Facebook’s “Scaling Memcache at Facebook” (NSDI 2013) for leases against thundering herds and stale sets, and the cache-aside “delete, don’t set” rule.

Related: probabilistic data structures (the Count-Min sketch W-TinyLFU uses), Redis client (eviction policies allkeys-lru / allkeys-lfu, which are sampled approximations), queueing and capacity (what a lower miss ratio does to the database’s queue), load balancing (consistent hashing and hot spots), storage engines (buffer pools and block caches), latency numbers, probability, Big-O.

Linking to an example

Share link copies a link that opens the tool exactly as you have it. Hand-written links work too: add the tab after # — curves, animate, write, stampede or tiers. Parameters you leave out keep their defaults.

Parameter Meaning Example

wl

Workload: zipf, loop, scan, shift, mix (or trace for an uploaded one)

wl=scan

keys, alpha, len, seed

Distinct keys, Zipf α, number of requests, random seed

alpha=1.1&keys=50000

loop, every, scan, phase, shift

Loop length; Zipf requests between scans and scan length; requests per phase and how far the hot set moves

wl=loop&loop=2000

pols

Policies, comma-separated: lru, lfu, fifo, clock, arc, 2q, tinylfu, s3fifo, opt

pols=lru,arc,opt

max, pts

Largest cache as % of distinct keys; number of sizes

max=5&pts=15

trace, pa, ca, pb, cb

Step through: the trace (spaces as +), policy and capacity of panes A and B (pb=none hides B)

trace=A+B+C+A&pa=arc&ca=2

wn, wa, wlen, wf, wcap, tc, tsr, tsw

Write policies: keys, α, requests, write fraction, cache size, cache / store-read / store-write latency (ms)

wf=0.5&tsw=20

sk, rate, ttl, delta, dbcap, dur, nodes, strat, jitter, beta, sseed

Stampede: keys, requests/s, TTL (s), recompute time (s), database concurrency limit, seconds simulated, app servers, strategy (none, node, global, xfetch, swr), TTL jitter (fraction), XFetch β, seed

strat=swr&rate=5000

tn, ta, trate, tnodes, l1, l2, shards, shardcap, t1, t2, tdb, sticky, copies

Tiers: keys, α, requests/s, app servers, L1 entries per server, L2 entries, Redis shards, shard capacity (ops/s), latencies (ms), route by key, copies of the hottest key

ta=1.2&l1=0

example

One of the presets below; parameters written beside it override it

example=loop-cliff

Presets (?example=…):

example= Shows

zipf-099

Every policy on Zipf α = 0.99 with OPT and Che’s approximation (open)

loop-cliff

A 1,000-key loop: LRU and FIFO hit nothing until it fits (open)

scan-resistance

Zipf with one-off scans: which policies keep the hot keys (open)

shifting-hot-set

The popular keys move every 25,000 requests (open)

belady-anomaly

FIFO: 9 misses with 3 frames, 10 with 4 (open)

arc-vs-lru

A scan through ARC and LRU side by side (open)

tinylfu-admission

W-TinyLFU rejecting one-hit wonders (open)

s3fifo-quick-demotion

S3-FIFO’s small and ghost queues at work (open)

write-back

A write-heavy mix: coalescing against dirty data at risk (open)

stampede

One hot key, 1,000 req/s, 200 ms recompute, no protection (open)

stampede-xfetch

The same with XFetch (open)

mass-expiry

2,000 keys filled together expire together (open)

mass-expiry-jitter

The same with ±20% TTL jitter (open)

hot-key

A hot key overloading one Redis shard (open)

hot-key-l1

The same traffic with a 1,000-entry L1 per server (open)

Runs entirely in your browser; nothing you type leaves this page.