ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 12

  1. Blog

nlargest vs sorted[:k]: Top-k Lab

Aditya Challa·30 September 2026·4 min read

Summary
On this page
  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — N=100 000 ints (p50 wall)
  5. Objects (N=100 000, k=100)
  6. Reading it
  7. N=10 000 sanity check
  8. Pitfalls
  9. When to pick what
  10. Reproduce
  11. Closing

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

ArmPattern
nlargestheapq.nlargest(k, data)
sorted slicesorted(data, reverse=True)[:k]
heap push/popsize-k min-heap of largest via heapreplace
objectssame with key= / (score, id) tuples

Lab topology

N in {10000, 100000}; k in {10, 100, 1000, 10000}
metric: p50 wall; elem/s = N / p50

Script: lab-evidence/66-nlargest-vs-sorted-slice/results/run_lab.py.


Lead table — N=100 000 ints (p50 wall)

knlargestsorted[:k]heap push/popnlargest÷sorted
102.28 ms16.34 ms4.60 ms7.16×
1002.43 ms16.48 ms4.66 ms6.77×
1 0004.00 ms16.21 ms5.59 ms4.05×
10 00016.82 ms16.24 ms11.05 ms0.97×

Objects (N=100 000, k=100)

Armp50 wallelem/s
nlargest + key4.58 ms21.8M
sorted + key24.37 ms4.1M
heap (score, id)7.22 ms13.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.
  • nlargest is 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

  1. sorted(...)[:k] out of habit for tiny k — pays a full sort.
  2. k growing with N — re-benchmark; crossover exists.
  3. Building a huge list then slicing — memory + time.
  4. Streaming data — online size-k heap / nlargest over an iterator still applies.

When to pick what

NeedPrefer
Fixed small kheapq.nlargest
k large (≳5–10% of N)sorted(..., reverse=True)[:k]
Streaming / one passsize-k heap
Full order needed anywaysort once

Reproduce

python3 lab-evidence/66-nlargest-vs-sorted-slice/results/run_lab.py

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.

heapq.nlargestsorted slicetop-kheapqpythonlocalhost labsreselection

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/.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

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

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — N=100 000 ints (p50 wall)
  5. Objects (N=100 000, k=100)
  6. Reading it
  7. N=10 000 sanity check
  8. Pitfalls
  9. When to pick what
  10. Reproduce
  11. Closing
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove