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).
Aditya Challa4 min read
Intro — what this post promises
Need a sorted collection with inserts and membership checks? This lab compares bisect.insort + bisect_left, a linear sorted insert, and a set (fast, but order-free) on Linux localhost — pure lookup, pure insert, and a 70/30 lookup/insert mix.
Related links:
- sorted vs heapq vs bisect localhost lab
- nlargest vs sorted slice localhost lab
- frozenset vs set membership localhost lab
- set vs list membership localhost lab
- itertools chain vs flatten localhost lab
- Counter vs dict tally localhost lab
- array vs list ints localhost lab
- perf_counter vs time localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. bisect on a list is O(log n) search but O(n) insert (element shift). Affiliates: 0. Complements the sorted/heapq/bisect overview with mixed workloads.
Verdict up front: lookup n=1 000 — set ~26M/s, bisect ~4.5M, linear ~0.24M (set ~5.8× bisect; bisect ~19× linear). Mixed s=2 000: set ~5.1× bisect; bisect ~181× linear-sorted insert.
Arms
| Arm | Keeps order? | Pattern |
|---|---|---|
| bisect | yes | bisect_left / insort |
| linear sorted insert | yes | scan + list.insert |
linear append + in | no after appends | unsorted growth |
| set | no | in / .add |
| sort-once | yes at end | append all then sort |
Lab topology
Script: lab-evidence/68-bisect-vs-linear-lookup/results/run_lab.py.
Lead table — lookup (p50)
| Arm | ops/s | ns/op |
|---|---|---|
| set n=1 000 | 26,145,418 | 38.2 |
| bisect n=1 000 | 4,470,967 | 223.7 |
| linear n=1 000 | 241,395 | 4142.6 |
| set n=10 000 | 24,302,211 | 41.1 |
| bisect n=10 000 | 3,504,040 | 285.4 |
| linear n=10 000 | 23,758 | 42090.4 |
Insert build-to-N (p50)
| Arm | ops/s | ns/op |
|---|---|---|
| append (unsorted) n=5 000 | 88,903,115 | 11.2 |
| set.add n=5 000 | 19,772,928 | 50.6 |
| sort once n=5 000 | 9,282,017 | 107.7 |
| insort n=5 000 | 2,673,781 | 374.0 |
Batch load? sort once ~3.5× repeated insort. Online sorted inserts pay the shift.
Mixed 70/30 (seed 2 000, 10 k steps)
| Arm | ops/s | ns/op |
|---|---|---|
| set | 15,631,179 | 64.0 |
| bisect | 3,075,871 | 325.1 |
linear append+in | 103,532 | 9658.8 |
| linear sorted insert | 17,029 | 58724.9 |
Reading it
- Need order + online inserts —
bisectbeats hand linear-sorted insert by ~181× on the mix; still loses to set on raw speed. - Need membership only —
set/frozenset(~6× bisect at n=1 k). - Batch then query sorted — append + one
sort, not N×insort(~3.5×). - insort is not free — logarithmic find + linear memmove.
Why list+bisect still exists
When you must binary-search a sequence, emit sorted output, or interop with APIs that want a list, bisect is the stdlib tool. For hot membership alone, paying O(n) shifts is the wrong tax — use a set (or a tree/skiplist if you need both order and faster inserts than a list).
Pitfalls
- Using bisect for membership-only maps — set wins.
- insorting a bulk load — sort once.
- Forgetting insert is O(n) — large n + many inserts → consider other structures.
- Assuming set preserves order — it does not (use sorted list / SortedDict recipes).
When to pick what
| Need | Prefer |
|---|---|
| Membership only | set |
| Sorted + rare inserts | list + bisect |
| Sorted + bulk build | append + sort |
| Sorted + heavy online inserts | specialized sorted container |
Reproduce
Evidence: /workspace/lab-evidence/68-bisect-vs-linear-lookup/results/.
Closing
Order costs. On this box set led bisect by ~5.8× on lookup and ~5.1× on a mixed workload, while bisect crushed linear-sorted insert by ~181×. Pick set for speed, bisect when sorted order is a product requirement.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5. lookup n=1000: set 26.1M bisect 4.5M linear 0.24M (set5.8x bisect; bisect19x linear). mixed s2000: set5.1x bisect; bisect181x linear-sorted. Affiliates: 0. Evidence: lab-evidence/68-bisect-vs-linear-lookup/.
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 50
signal vs threading.Event Wakeup: Localhost Lab
Hands-on signal SIGUSR1 vs threading.Event wakeup lab: real p50 latency in microseconds, 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