Plate 51
frozenset vs set: Membership Lab
Hands-on frozenset vs set vs list membership lab: real ops/s for build-once lookup-heavy paths and dict keys, benchmarked on Linux localhost for SREs.
Aditya Challa4 min read
Intro — what this post promises
After you build once, is frozenset any slower than set for repeated in checks? This lab times lookup-heavy membership on Linux localhost for set, frozenset, and a list baseline — plus frozenset as a dict key (something set cannot do).
Related links:
- set vs list membership localhost lab
- enum vs constants localhost lab
- contextlib vs try/finally localhost lab
- itemgetter vs lambda sort localhost lab
- lru_cache hit vs miss localhost lab
- ThreadPoolExecutor vs sequential localhost lab
- array vs list ints localhost lab
- itertools vs python loops localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. 200,000 probes per lookup arm; sizes 64 / 1 024 / 10 000. Containers built once outside the timer. Affiliates: 0. Complements the set-vs-list lab by adding frozenset and hashability.
Verdict up front: at n=1 024, set ~23.3M/s and frozenset ~23.2M (~0.997× — a wash). List ~0.26M (~90× behind). At n=10 000 set vs list ~821×. Prefer frozenset when you need immutability or dict/set nesting.
Arms
| Arm | Pattern |
|---|---|
set lookup | x in s after set(members) |
frozenset lookup | x in fs after frozenset(members) |
list lookup | linear x in lst baseline |
| build | set(src) / frozenset(src) from list |
| dict key | frozenset key vs tuple-of-sorted stand-in |
Lab topology
Script: lab-evidence/62-frozenset-vs-set-membership/results/run_lab.py.
Lead table — lookup (p50)
| Arm | ops/s | ns/op |
|---|---|---|
| set n=64 | 25,477,480 | 39.3 |
| frozenset n=64 | 26,037,110 | 38.4 |
| list n=64 | 3,899,245 | 256.5 |
| set n=1 024 | 23,272,129 | 43.0 |
| frozenset n=1 024 | 23,211,976 | 43.1 |
| list n=1 024 | 257,154 | 3888.7 |
| set n=10 000 | 20,921,583 | 47.8 |
| frozenset n=10 000 | 20,850,561 | 48.0 |
| list n=10 000 | 25,486 | 39236.7 |
Build cost (from list)
| Arm | builds/s | ns/build |
|---|---|---|
| set n=1 024 | 142,299 | 7027.5 |
| frozenset n=1 024 | 143,145 | 6985.9 |
| set n=10 000 | 12,288 | 81379.8 |
| frozenset n=10 000 | 12,278 | 81444.9 |
Build set vs frozenset at n=1 024: ~0.994× — also a wash. Pay build once; amortize over lookups.
Hashable bonus — dict keys
| Arm | ops/s | ns/op |
|---|---|---|
| frozenset as dict key | 28,092,348 | 35.6 |
| tuple(sorted(...)) key | 18,824,050 | 53.1 |
set raises TypeError as a dict key (unhashable). Frozenset keyed ~1.49× a sorted-tuple stand-in here.
Reading it
- Lookup parity — frozenset and set share the same hash-table story (~1.00× across sizes).
- List dies with n — ~90× at 1 024, ~821× at 10 000.
- Pick frozenset for API contracts — “this membership set will not mutate” and nestable in other sets/dicts.
- Pick set when you mutate —
.add/.discardin place.
Pitfalls
- Rebuilding the set every request — then build cost dominates; cache a frozenset.
- Using list for “small” membership — even n=64 was ~6.5× faster as a set here.
- Assuming frozenset is slower — not on the lookup path in this lab.
- Putting a
setin a dict — hashability, not speed, forces frozenset.
When to pick what
| Need | Prefer |
|---|---|
Hot in checks, may mutate | set |
Hot in checks, fixed membership | frozenset |
| Dict/set of membership sets | frozenset keys |
| Tiny n, one-shot scan | list (rarely) |
Reproduce
Evidence: /workspace/lab-evidence/62-frozenset-vs-set-membership/results/.
Closing
frozenset ≈ set on lookups. On this box n=1 024 both sat near ~23M/s while list lagged ~90×; at n=10 000 the list gap grew to ~821×. Use frozenset when immutability or hashability matters — not because it is faster.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5; probes=200000. n=1024 lookup set 23.3M frozenset 23.2M (~0.997x); set vs list ~90x; n=10k set vs list ~821x. Affiliates: 0. Evidence: lab-evidence/62-frozenset-vs-set-membership/.
Related links
Plate 09
set vs frozenset vs list: Membership Lookup Lab
A hands-on localhost lab measuring set, frozenset, list, and dict-key membership across N, with the real crossover point.
Observability & SRE · 30 Sept 2026
Plate 28
bisect vs Linear vs set: Lookup Lab
Hands-on bisect.insort vs linear vs set lab: real ops/s for mixed sorted-list insert and membership lookup workloads, measured on Linux localhost (lab).
30 Sept 2026
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