Plate 12
nlargest vs sorted[:k]: Top-k Lab
Aditya Challa4 min read
Intro — what this post promises
Need the top-k of N values: is heapq.nlargest(k, …) faster than sorted(…, reverse=True)[:k]? This lab varies N and k on Linux localhost, and compares a hand-rolled size-k heap (heappush / heapreplace). Complements the broader sorted/heapq/bisect lab with a top-k focus.
Related links:
- sorted vs heapq vs bisect localhost lab
- Counter vs dict tally localhost lab
- attrgetter vs getattr localhost lab
- itemgetter vs lambda sort localhost lab
- frozenset vs set membership localhost lab
- bytes vs bytearray localhost lab
- itertools vs python loops localhost lab
- perf_counter vs time localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. Wall times are p50 over 9 runs. Affiliates: 0. When k ≈ N, expect sorted to catch up — selection is not free at large k.
Verdict up front (N=100 000): k=10 nlargest ~2.28 ms vs sorted slice ~16.34 ms (~7.2×); k=1 000 ~4.1×; k=10 000 ~0.97× (sorted slightly ahead). Objects with key=: nlargest ~5.3× sorted.
Arms
| Arm | Pattern |
|---|---|
nlargest | heapq.nlargest(k, data) |
| sorted slice | sorted(data, reverse=True)[:k] |
| heap push/pop | size-k min-heap of largest via heapreplace |
| objects | same with key= / (score, id) tuples |
Lab topology
Script: lab-evidence/66-nlargest-vs-sorted-slice/results/run_lab.py.
Lead table — N=100 000 ints (p50 wall)
| k | nlargest | sorted[:k] | heap push/pop | nlargest÷sorted |
|---|---|---|---|---|
| 10 | 2.28 ms | 16.34 ms | 4.60 ms | 7.16× |
| 100 | 2.43 ms | 16.48 ms | 4.66 ms | 6.77× |
| 1 000 | 4.00 ms | 16.21 ms | 5.59 ms | 4.05× |
| 10 000 | 16.82 ms | 16.24 ms | 11.05 ms | 0.97× |
Objects (N=100 000, k=100)
| Arm | p50 wall | elem/s |
|---|---|---|
| nlargest + key | 4.58 ms | 21.8M |
| sorted + key | 24.37 ms | 4.1M |
| heap (score, id) | 7.22 ms | 13.8M |
Reading it
- Small k ⇒ nlargest wins big — avoids full O(N log N) sort (~7× at k=10).
- Large k ⇒ sorted catches up — at k=10 000 (10% of N) sorted was ~1.04× nlargest; hand heap even led.
- Hand size-k heap sits between them for small k; at huge k its O(N log k) constant lost to Timsort.
nlargestis the readable default for “top 10 / top 100” dashboards.
N=10 000 sanity check
At smaller N the same pattern holds: k=10 nlargest was about 5× sorted slice; by k=1 000 the gap shrank to roughly 1.1×. Hand size-k heap beat nlargest once k got large relative to N (k=1 000 on N=10 k) — another reminder that constants and k/N ratio matter more than the brand name on the function.
Also note ascending sorted(data)[-k:] matched reverse-slice wall time in the N=100 k arms we sampled — the cost is the full sort, not the direction flag.
Pitfalls
sorted(...)[:k]out of habit for tiny k — pays a full sort.- k growing with N — re-benchmark; crossover exists.
- Building a huge list then slicing — memory + time.
- Streaming data — online size-k heap /
nlargestover an iterator still applies.
When to pick what
| Need | Prefer |
|---|---|
| Fixed small k | heapq.nlargest |
| k large (≳5–10% of N) | sorted(..., reverse=True)[:k] |
| Streaming / one pass | size-k heap |
| Full order needed anyway | sort once |
Reproduce
Evidence: /workspace/lab-evidence/66-nlargest-vs-sorted-slice/results/.
Closing
Top-k is not always a sort. On this box N=100 k / k=10 nlargest beat sorted slice ~7.2×; by k=10 k they were within ~3%. Match the algorithm to how small k really is.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5. N=100k k=10: nlargest 2.28ms vs sorted[:k] 16.34ms (~7.2x); k=1000 ~4.1x; k=10k ~0.97x (sorted catches up). Affiliates: 0. Evidence: lab-evidence/66-nlargest-vs-sorted-slice/.
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