4 min read
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:
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.
| 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 |
Script: lab-evidence/71-zip-vs-index-pairing/results/run_lab.py.
| 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.
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.
| 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.
zip is the default pairing API — readable and usually as fast or faster on consume.strict=True is nearly free on equal lengths here — use it to catch bugs.range/enumerate on one sequence; avoid enumerate(zip) unless you need both.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.
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.
zip on unequal inputs — prefer strict=True.enumerate(zip(a,b)) when i unused — extra work.list(zip(...)) just to iterate once — waste.| 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 |
Evidence: /workspace/lab-evidence/71-zip-vs-index-pairing/results/.
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
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/.
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
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.
Summary1 Oct 20264 min read
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