ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 68

  1. Blog

WeakValueDictionary vs dict Cache: Localhost Lab

WeakValueDictionary vs strong dict cache: insert and lookup throughput, plus GC-driven drop on Linux localhost.

Aditya Challa·30 September 2026·4 min read

Summary
On this page
  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — n=10 000 (p50)
  5. Scale sketch
  6. GC drop (the point of weak caches)
  7. Reading it
  8. When weakrefs help
  9. Pitfalls
  10. Reproduce
  11. Limits
  12. Takeaway

Intro — what this post promises

Can weakref.WeakValueDictionary replace a strong dict as a cache-like map? This lab measures insert and lookup ops/s on Linux localhost, then shows entries vanishing after del + gc.collect() when only weak refs remain.

Related links:

  • lru cache hit miss localhost lab
  • threading local vs dict localhost lab
  • setdefault vs defaultdict localhost lab
  • decimal vs float sum localhost lab
  • pickle vs json roundtrip localhost lab
  • asyncio gather vs taskgroup localhost lab
  • counter vs dict tally localhost lab
  • csv reader vs split localhost lab

Lab honesty (1 Oct 2026 IST): Python 3.13.5. Values are small Payload objects with __weakref__. Affiliates: 0. No Docker. Not an LRU eviction policy lab (see lru_cache lab).

Verdict up front (n=10 000): strong insert ~6027.1 k/s vs weak ~960.1 k/s (~6.28×); strong lookup ~16.37 M/s vs weak ~7.64 M/s (~2.14×). After dropping all strong refs to values and gc.collect(), weak map went 10000 → 0 (dropped 10000).


Arms

ArmPattern
strong dictd[k] = obj / d[k].n
WeakValueDictionarysame API; values held weakly
GC dropbuild weak map, del object list, gc.collect(), recount

Lab topology

n in {1000, 10000, 50000} · 7 rounds · p50
insert includes Payload construction
lookup holds strong refs so weak map stays full
GC demo at n=10000 with only-weak ownership

Script: lab-evidence/86-weakref-vs-dict-cache/results/run_lab.py.


Lead table — n=10 000 (p50)

Armops/s
strong insert6027.1 k
weak insert960.1 k (~6.28× slower)
strong lookup16.37 M
weak lookup7.64 M (~2.14× slower)

Scale sketch

nstrong insert k/sweak insert k/sstrong lookup M/sweak lookup M/s
1 0006966.71041.031.0712.22
10 0006027.1960.116.377.64
50 0002706.7971.525.1310.91

Insert tax for weak maps stayed roughly ~6× at 10 k; lookup closer to ~2×.


GC drop (the point of weak caches)

Scenario B — only the weak map holds values:

  1. Insert 10 000 objects into WeakValueDictionary, keep a list of strong refs.
  2. del the list; gc.collect().
  3. len(weak) went 10000 → 0.

If a strong dict still owns the same objects, weak entries stay alive — weakrefs do not override other strong owners. That is why “cache” designs often pair weak value maps with an explicit size/TTL policy, not weakrefs alone.


Reading it

  • Strong dict wins raw speed for insert/lookup microbenches.
  • WeakValueDictionary buys auto-drop when nothing else references the value — pay ~2–6× on this box.
  • Use weak value maps for identity caches / registries where leaking objects is worse than a miss; use functools.lru_cache / explicit eviction when you need bounded strong residency.

When weakrefs help

GoalPrefer
Fast hot map, you own lifetimestrong dict
Avoid retaining objects nobody else needsWeakValueDictionary
Bounded memo of pure functionslru_cache
Key-side weakWeakKeyDictionary (not measured here)

Pitfalls

  • Storing objects that cannot be weakly referenced (some builtins/extension types).
  • Expecting weak maps to bound memory while another structure still holds strong refs.
  • Looking up after GC without handling KeyError / missing keys.
  • Comparing to LRU without measuring hit-rate economics.

Reproduce

python3 lab-evidence/86-weakref-vs-dict-cache/results/run_lab.py

Evidence: summary.json, summary.txt.


Limits

One Linux box. Synthetic Payload with slots+__weakref__. Not multi-process. GC timing can vary under load.


Takeaway

Strong dict was about ~6.28× faster to fill and ~2.14× faster to read at n=10 k. WeakValueDictionary dropped all 10000 entries once strong refs vanished — that auto-cleanup is the product, not the Mops/s. Pick weak maps for leak resistance; pick strong maps (or LRU) for speed and explicit residency.

weakvaluedictionaryweakrefdict cachegc droppython cachelocalhost labsreops/s

Lab evidence

What I found running this

Lab 1 Oct 2026 IST. Python 3.13.5. n=10k: strong insert 6027.1k/s vs weak 960.1k/s (~6.28x); lookup 16.37M vs 7.64M (~2.14x). GC: weak 10000->0 after del+gc. Affiliates: 0. Evidence: lab-evidence/86-weakref-vs-dict-cache/.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Related links

  • Plate 17

    platform vs os.uname Inventory: Localhost Lab

    Hands-on platform.platform vs os.uname host inventory lab: real ops/s plus cache notes, measured on Linux localhost today in this hands-on lab for SREs.

    1 Oct 2026

  • Plate 75

    uuid.uuid4 vs uuid.uuid1: Localhost Lab

    Hands-on uuid.uuid4 vs uuid.uuid1 ID generation lab: real ops/s plus version/node checks, measured on Linux localhost today in this hands-on lab for SREs.

    1 Oct 2026

  • Plate 76

    cmath vs math.hypot Magnitudes: Localhost Lab

    Hands-on cmath vs math.hypot magnitude ops lab: real ops/s for abs, polar, and phase, measured on Linux localhost today in this hands-on lab for SREs.

    1 Oct 2026

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — n=10 000 (p50)
  5. Scale sketch
  6. GC drop (the point of weak caches)
  7. Reading it
  8. When weakrefs help
  9. Pitfalls
  10. Reproduce
  11. Limits
  12. Takeaway
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove