Hands-on collections.deque vs list queue lab: real FIFO ops/s for append/popleft vs list.pop(0) at several prefill sizes on Linux localhost (eng lab).
4 min read
For a FIFO queue in Python, collections.deque beats a list: with append + popleft vs append + pop(0) on Python 3.13.5, deque was about 1.2x faster on a near-empty queue, 2.6x at 1,000 queued items and about 31x at 10,000 (18.6M vs 605k cycles/s). The problem is removing from the front of a list, not lists in general, because list.pop() from the end drained 50k items as fast as deque did.
Related links:
Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. FIFO cycle = one append + one front pop, keeping length ≈ S.
Verdict up front: at S=0–100 deque is only ~1.2–1.2× list. At S=1 000 already ~2.6×. At S=10 000 ~31× (18.6M vs 605k cycles/s). Draining 50k with pop(0) is ~255k/s vs deque ~31.6M.
| Arm | Pattern |
|---|---|
| FIFO | prefill S, then M× (append + pop front) |
| drain | prefill N, pop until empty |
| append-only | grow from empty (sanity) |
| list pop() end | drain with pop() — stack, not queue |
Script: lab-evidence/54-deque-vs-list-queue/results/run_lab.py.
| Prefill S | deque | list pop(0) | deque÷list |
|---|---|---|---|
| 0 | 34,942,188 | 28,243,593 | 1.2× |
| 100 | 34,512,082 | 28,976,033 | 1.2× |
| 1,000 | 32,741,948 | 12,494,584 | 2.6× |
| 10,000 | 18,638,981 | 604,501 | 30.8× |
| 50,000 | 3,049,348 | 119,596 | 25.5× |
At S=10k, list pays ~1654 ns/cycle vs deque ~54 ns.
| Arm | ops/s | ns/op |
|---|---|---|
| deque drain N=50k | 31,573,530 | 31.7 |
| list pop(0) drain N=50k | 255,487 | 3914.1 |
| list pop() end N=50k | 36,831,951 | 27.2 |
| list append N=100k | 41,751,387 | 24.0 |
| deque append N=100k | 36,859,592 | 27.1 |
list.pop() from the end matches deque drain speed — the bug is front removal, not “lists are slow.”
pop()) with queue (pop(0)) — different asymptotics.| Need | Prefer |
|---|---|
| FIFO / sliding window | collections.deque |
| Stack / LIFO | list + append/pop |
| Random access + occasional ends | list |
| Append-only buffer | either (append is peer) |
Evidence: /workspace/lab-evidence/54-deque-vs-list-queue/results/.
list.pop(0) does not stay fine. On this box deque FIFO is ~2.6× at S=1k and ~31× at S=10k. Drain 50k: deque ~32M/s vs list pop(0) ~255k. Use deque for queues; keep list for stacks and random access.
For queue work, yes. Draining 50,000 items, deque.popleft ran at about 31.6M pops/s vs about 255k/s for list.pop(0). For appends only, the two are close (41.8M vs 36.9M/s).
Removing the first item shifts every remaining item, so the cost grows with queue length. At 10,000 items a list cycle cost about 1,654 ns vs about 54 ns for deque.
For stacks (append/pop from the end) and random access. Indexing into the middle of a deque is O(n), so use a list when you need positional reads.
Lab evidence
Lab 1 Oct 2026 IST. Python 3.13.5. FIFO append+popleft/pop(0). S=0: deque 34.9M vs list 28.2M (~1.24x). S=1000: deque 32.7M vs list 12.5M (~2.6x). S=10000: deque 18.6M vs list 605k (~31x). S=50000: ~25x. Drain N=50k: deque 31.6M vs list pop(0) 255k. Affiliates: 0. Evidence: lab-evidence/54-deque-vs-list-queue/.
Explore the product
Untested by us. Check what's included, the license and refund terms before you buy.
Hands-on itertools.islice vs list slice window lab: real ops/s taking ranges from sequences, measured on Linux localhost in this hands-on lab for SREs.
Lab1 Oct 20264 min read
Hands-on heapq.merge vs sorted(chain) multi-way merge: real records/s on pre-sorted lists, measured on Linux localhost today in this hands-on lab for SREs.
Lab1 Oct 20264 min read
Hands-on mmap MAP_SHARED write+msync vs pwrite region update: real MB/s with durability labels, measured on Linux localhost in this hands-on lab for SREs.
Lab1 Oct 20264 min read