Plate 18
zip vs Index Pairing: Loop Lab
Aditya Challa4 min read
Intro — what this post promises
Pairing two sequences: is zip faster than for i in range(n): (a[i], b[i]), and what does strict=True cost? This lab times materialize (list(zip)) and consume loops on Linux localhost.
Related links:
- itertools chain vs flatten localhost lab
- functools partial vs lambda localhost lab
- itertools vs python loops localhost lab
- nlargest vs sorted slice localhost lab
- array vs list ints localhost lab
- statistics vs manual mean localhost lab
- bisect vs linear lookup localhost lab
- perf_counter vs time localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. One op = one pair. Affiliates: 0. zip truncates to the shorter input; strict=True errors on length mismatch.
Verdict up front (N=100 000 consume): zip ~26.8M/s vs index ~23.8M (~1.13×); list(zip) ~1.15× index comprehension. strict=True ≈ plain zip on equal-length consume (~0.98×). Prefer zip for clarity; index when you already need i.
Arms
| Arm | Pattern |
|---|---|
list(zip(a,b)) | materialize pairs |
list(zip(..., strict=True)) | length-checked zip |
| index comprehension | [(a[i], b[i]) for i in range(n)] |
| index append loop | manual append |
| consume zip / index | sum x+y without list of tuples |
enumerate(zip(...)) | index + pair |
Lab topology
Script: lab-evidence/71-zip-vs-index-pairing/results/run_lab.py.
Lead table — consume N=100 000 / 1 000 000 (p50)
| Arm | N=100k ops/s | N=1M ops/s |
|---|---|---|
| zip | 26,785,953 | 26,827,121 |
| zip strict | 27,347,675 | 26,747,626 |
| index | 23,798,497 | 23,709,280 |
| enumerate(zip) | 14,214,237 | 14,218,640 |
Index alone beat enumerate(zip) by ~1.7× — do not pay enumerate if you only need the parallel elements.
Materialize list(...) N=100 000
| Arm | ops/s | ns/op |
|---|---|---|
| zip | 15,927,092 | 62.8 |
| zip strict | 18,122,229 | 55.2 |
| index comp | 13,894,451 | 72.0 |
| index append | 13,721,415 | 72.9 |
At N=1M materialize, index comp edged zip (zip÷index ~0.79×) — allocation/GC noise shows up; consume path still favored zip.
Unequal lengths & 3-way
| Arm | ops/s |
|---|---|
| zip truncate (shorter) | 18,035,546 |
index min(len) | 15,050,812 |
| consume zip3 | 19,816,798 |
| consume index3 | 17,650,335 |
zip truncate ~1.20× index-min; 3-way zip ~1.12× index3.
Reading it
zipis the default pairing API — readable and usually as fast or faster on consume.strict=Trueis nearly free on equal lengths here — use it to catch bugs.- Need the index anyway —
range/enumerateon one sequence; avoidenumerate(zip)unless you need both. - Materialize only if you need a list of pairs — otherwise iterate zip directly.
Why index sometimes “wins” on huge list(zip)
Building millions of tuple objects dominates. Small timing flips between zip and index comps at N=1M are about allocator behavior, not a reason to rewrite clear zip loops. Optimize the consume shape you actually run in production.
strict=True as production default
Equal-length zip is the common case; the bug is silently dropping a tail when lengths drift after a refactor. On this box the consume overhead of strict=True was noise (~1.00×). Prefer zip(a, b, strict=True) in new code unless you intentionally want truncation — and if you do, comment why.
Pitfalls
- Silent truncation with plain
zipon unequal inputs — preferstrict=True. enumerate(zip(a,b))wheniunused — extra work.- Building
list(zip(...))just to iterate once — waste. - Assuming index is always slower — microbenches vary; API clarity still favors zip.
When to pick what
| Need | Prefer |
|---|---|
| Pair / parallel iterate | zip |
| Assert equal lengths | zip(..., strict=True) |
| Need index + one sequence | enumerate(seq) |
| Need index into multiple lists | range(n) or enumerate + index |
Reproduce
Evidence: /workspace/lab-evidence/71-zip-vs-index-pairing/results/.
Closing
zip for pairing; strict for safety; index when you need i. On this box N=100 k consume zip led index by ~1.13×, strict matched zip, and enumerate(zip) trailed bare index by ~1.7×. Write zip first; reach for indices when the algorithm needs them.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5. N=100k consume zip 26.8M vs index 23.8M (~1.13x); list(zip) vs index-comp ~1.15x; strict ≈ zip on consume. Affiliates: 0. Evidence: lab-evidence/71-zip-vs-index-pairing/.
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