ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
Topics
sre111localhost lab102performance70python58javascript47bundle size36benchmarking35node.js35web development21typescript19benchmark16content creation15All topics
About
Loading article…

ShopperCove

Hands-on notes on developer tools, written after the tool has actually been run.

All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove

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
  1. Blog

WeakValueDictionary vs dict Cache: Localhost Lab

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

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:

  • 01shoppercove.comlru cache hit miss localhost lab
  • 02shoppercove.comthreading local vs dict localhost lab
  • 03shoppercove.comsetdefault vs defaultdict localhost lab
  • 04shoppercove.comdecimal vs float sum localhost lab
  • 05shoppercove.compickle vs json roundtrip localhost lab
  • 06shoppercove.comasyncio gather vs taskgroup localhost lab
  • 07shoppercove.comcounter vs dict tally localhost lab
  • 08shoppercove.comcsv 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.

Keep reading

  • 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.

    Summary1 Oct 20264 min read

  • 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.

    Summary1 Oct 20263 min read

  • 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.

    Summary1 Oct 20263 min read