4 min read
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:
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.
| 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 |
Script: lab-evidence/66-nlargest-vs-sorted-slice/results/run_lab.py.
| 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× |
| 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 |
nlargest is the readable default for “top 10 / top 100” dashboards.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.
sorted(...)[:k] out of habit for tiny k — pays a full sort.nlargest over an iterator still applies.| 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 |
Evidence: /workspace/lab-evidence/66-nlargest-vs-sorted-slice/results/.
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
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/.
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