Powersort
Nearly-optimal run-adaptive stable sorting — now powering Python 3.11+
watch it workEvery array hides a
perfectly balanced tree.
Powersort sorts by merging the runs — already-sorted stretches — that real-world data is full of. To decide which runs to merge first, it assigns each boundary between runs a power: its depth in a virtual, perfectly balanced binary tree laid over the array. Watch it drop into place.
1 runs
A single left-to-right scan chops the input into maximal sorted runs (descending runs are reversed on the fly). Nearly-sorted data yields few, long runs — and Powersort gets faster the fewer there are.
2 node powers
Imagine halving the array again and again: the midpoint has power 1, the quarter points power 2, and so on. No tree is ever built — a node's position and depth follow from pure arithmetic on indices.
3 boundary powers
The power of the boundary between two adjacent runs is the depth of the shallowest node lying between their midpoints — computed in a few machine instructions. Low power ⇒ merge late; high power ⇒ merge early.
What is Powersort?
Powersort is a natural run-based mergesort algorithm with provably near-optimal merge cost. It was designed by J. Ian Munro and Sebastian Wild and presented at ESA 2018. In Python 3.11, it replaced Timsort's merge policy in CPython, and it is also widely used beyond CPython (including Apple's WebKit, PyPy and NumPy).
Adaptive to Existing Order
Exploits existing order present in the data, requiring far fewer comparisons on partially-sorted inputs.
Provably Nearly Optimal
Its mathematically grounded node-power merge policy builds merge trees with provable merge cost at most n · H + 2n — sorting with n · H + 3n ≤ n log₂ n + O(n) comparisons, where H is the run-length entropy of the input.
Python 3.11 Default
Adopted in CPython 3.11 as a drop-in improvement over Timsort's original merge strategy, with zero API changes for users.
How the Algorithm Works
Like Timsort, Powersort scans the input for natural runs — maximal ascending (or reversed-descending) ranges. The innovation is in how those runs are merged: instead of Timsort's stack-based heuristic, Powersort uses a carefully computed run-boundary power to schedule merges in a near-optimal order.
Detect Natural Runs
Scan left-to-right. A run is a maximal non-decreasing subsequence
(descending runs are reversed in-place). If a run is shorter than
MIN_RUN (≈ 32–64 elements) it is extended using binary insertion sort.
Compute Node Power
For two adjacent runs A and B in an array of total length n, the node power is computed from their start positions and lengths. Intuitively, it represents the level in a balanced binary tree at which the interval spanned by A ∪ B would first appear.
Maintain a Run Stack
Runs are pushed onto a stack. Before pushing a new run, any pending runs on the stack whose node power is greater than or equal to the new run's power are merged first. This guarantees the merge tree is nearly optimal.
Flush Remaining Runs
After the entire input has been scanned, all remaining runs on the stack are merged right-to-left, completing the sort.
Try it — Powersort in action
Runs are discovered one at a time and kept on a run stack. When a new boundary's power is no larger than the power on top of the stack, the two topmost runs merge — building the merge tree from left to right. Try your own input and watch the policy decide.
Press play to run Powersort.
Note: for clarity this demo uses ascending runs only — real implementations also reverse descending runs (and enforce a minimum run length, see step 1 above).
? why it works
The stack keeps boundary powers strictly increasing from bottom to top, so every merge is a node of the merge tree "closed off" as soon as the scan passes it. Runs are touched only when merged — the whole policy costs O(1) extra work per run.
≈ nearly optimal
The resulting merge tree is within a constant number of comparisons per element of the best possible merge order for those runs — Powersort inherits Timsort's galloping merges but provably avoids its unbalanced-merge worst cases.
🐍 in your Python
Since Python 3.11, CPython's built-in list.sort() and
sorted() use Powersort's merge policy — so this exact rule runs
billions of times a day.
Given an array of length n, two adjacent runs starting at position
i and j = i + |A| (where |A| is the length
of run A), the run-boundary power k is the number of leading identical bits in
the binary representations of the fractions
(i + |A|/2) / n and (j + |B|/2) / n.
It can be computed as follows (for a more efficient solution, see below):
import math
def power(run1, run2, n):
i1 = run1[0]; n1 = run1[1]
i2 = run2[0]; n2 = run2[1]
assert n1 >= 1 and n2 >= 1
assert i1 >= 0 and i2 == i1 + n1 and i2 + n2 <= n
a = (i1 + n1/2) / n
b = (i2 + n2/2) / n
l = 0
while math.floor(a * 2**l) == math.floor(b * 2**l):
l += 1
return l
A higher node power means the two runs should be merged sooner (at a deeper level of the merge tree). The stack invariant ensures that merges always happen in the right order.
Comparison with Timsort merge policy
| Property | Timsort | Powersort |
|---|---|---|
| Merge policy | Ad-hoc heuristic (4 length-based rules) | Node-power merge policy |
| Optimality guarantee | ≤ 1.5 · OPT + O(n) merge cost | ≤ OPT + O(n) merge cost |
| Worst-case comparisons | 1.5 · n log₂ n + O(n) | n log₂ n + O(n) |
| Adaptive to runs | Yes (but not optimally) | Yes (near-optimal) |
| Algorithm complexity | Complex stack invariants, hard to prove correct | Simple, clean invariant |
| Stable sort | Yes | Yes |
| Extra memory | O(n) | O(n) |
🪲 Timsort's troubled merge policy
Part of the reason why Powersort was so welcomed is the troublesome history of Timsort's original merge policy, which has been a source of bugs and confusion in both Python and Java implementations. For many years, the standard library sorting methods were susceptible to a stack overflow from adversarial inputs!
While Timsort was eventually patched, Powersort's mathematically grounded merge policy elegantly resolves this issue from first principles, making the formal proof for correctness and needed stack size robust and intuitive.
Powersort in Python 3.11
Python's built-in list.sort() and sorted() have used Timsort
since Python 2.3. With Python 3.11 (released October 2022),
the CPython team adopted Powersort's merge strategy, making every Python program that
sorts data an implicit beneficiary of this research.
What changed?
- The merge-decision logic inside Timsort's run-stacking loop was replaced with the node-power computation — a single integer loop needing no floating-point arithmetic.
- Timsort's complex merge invariants (stack-balance checks) were simplified while retaining all existing optimisations such as galloping merges and binary insertion sort for short runs.
- Performance is equivalent or faster across benchmark sets, with measurable wins on adversarial sequences that Timsort handled sub-optimally (see benchmark discussion and bpo-34561).
- Full backwards compatibility: the sort is still stable, in-place (logically), and respects custom key functions and comparison operators.
Try it yourself
# Python 3.11+ uses Powersort under the hood automatically
import sys
print(sys.version) # 3.11.x ...
data = [5, 3, 1, 4, 1, 5, 9, 2, 6]
data.sort() # Powersort at work!
print(data) # [1, 1, 2, 3, 4, 5, 5, 6, 9]
Implementations & Code
Powersort is used in a growing number of language runtimes, standard libraries, and scientific/systems software. All links below are to open-source code.
PyPy source (listsort.py)
Powersort has been adapted by Carl Friedrich Bolz-Tereick for
PyPy.
His implementation is simultaneously the production code for the PyPy Python
implementation and is a very readable reference implementation of the algorithm.
See the Python source on GitHub in rpython/rlib/listsort.py.
CPython 3.11+ (Objects/listobject.c)
The production C source code powering list.sort() in every default
Python 3.11+ installation. Look for powerloop in the source.
WebKit / JavaScriptCore (StableSort.h)
Apple's JavaScriptCore engine (used in Safari/WebKit) implements Powersort in a
templated arrayStableSort function, computing run-boundary power via
a dedicated power lambda and merging with
mergePowersortRuns. Short runs fall back to a bundled insertion sort.
The source cites the ESA 2018 paper (LIPIcs DOI) directly.
NumPy (numpy/_core/src/npysort/timsort.hpp)
NumPy adopted Powersort's merge strategy in its sort implementation.
See powerloop and found_new_run_ in the source,
introduced via
NumPy PR #29208.
AssemblyScript standard library (util/sort.ts)
The WebAssembly-targeting language's standard library implements Powersort in a
SORT<T>() function, with nodePower() and
mergeRuns() helpers, backing Array<T>.sort(). The
source comment cites the Munro–Wild paper directly.
Hare standard library (sort/sort.ha)
Hare's standard library implements Powersort in a powersort function
with a node_power helper, used for slices of 256 elements or more
(shorter slices fall back to binary insertion sort). The source cites the
original Munro–Wild paper directly in a comment.
Kuroko (src/obj_list.c)
The Kuroko scripting language implements Powersort in its C runtime via a static
powersort function, with powersort_power and
powersort_merge helpers, backing both the list.sort()
method and the built-in sorted() function. The implementation
credits the Munro–Wild paper and Sebastian Wild's reference implementation
directly in comments.
Nix (libutil/sort.hh)
The Nix package manager's utility library implements Peeksort, a
recursive divide-and-conquer variant, via a peeksort function and merge
helper mergeSortedRunsInPlace. The implementation is explicitly
adapted (MIT-licensed) from Sebastian Wild's reference implementation and cites the
same underlying paper.
Minimal Python sketch
"""
Minimal illustrative Powersort sketch.
For helper functions, see https://tiny.cc/timsort.
"""
import math
def power(run1, run2, n):
# Compute the "power" of the boundary between run1 and run2 .
# Input: adjacent runs run1, run2 from a list of total length n.
# See listsort.txt for details; code follows CPython.
i1 = run1[0]; n1 = run1[1]
i2 = run2[0]; n2 = run2[1]
a = 2 * i1 + n1
b = a + n1 + n2
l = 0
while True:
l += 1
if a >= n:
assert b >= a
a -= n; b -= n
elif b >= n:
break
assert a < b < n
a <<= 1; b <<= 1
return l
def powersort(a, extend_run=extend_run):
"""
Sort a list using powersort.
Note: Unlike CPython's version, this variant stores the run-boundary power
in the right run. This removes the need for updating powers upon merges.
"""
n = len(a)
i = 0
runs = [] # stack of (start, length, power) tuples
j = extend_run(a, i)
runs.append((i, j-i, 0))
i = j
while i < n:
j = extend_run(a, i)
p = power(runs[-1], (i,j-i), n)
while p <= runs[-1][2]:
merge_topmost_2(a, runs)
runs.append((i,j-i,p))
i = j
while len(runs) >= 2:
merge_topmost_2(a, runs)
Powersort-Based Hybrid Sorts
These implementations don't use Powersort as their whole sorting algorithm, but embed its node-power merge policy as one component inside a larger hybrid design.
Rust standard library — driftsort (stable/drift.rs)
Rust's slice::sort uses driftsort, a hybrid design by
Orson Peters and Lukas Bergdoll that combines lazy top-down quicksort partitioning
with bottom-up merging (adapted from glidesort). Its top-level run-merging loop is
explicitly Powersort's node-power heuristic — the module doc comment states
this directly — layered underneath additional machinery for low-cardinality
inputs, panic safety, and binary-size/compile-time tuning.
Reference & Research Implementations
Not shipped in a production language or library, but built for benchmarking, study, or as a starting point for further ports.
Java implementation
A Java implementation suitable for benchmarking against Java's standard
Arrays.sort, also developed by Sebastian Wild as part of the
research artefacts for the ESA 2018 paper.
C++ implementation
Low-level C++ implementation repository with variants useful for experiments and direct comparison against other implementations.
Algorithm Variants
The details of Powersort's inner workings make further novel implementation variants possible. A key question is where to put your data; fast stable merging requires a buffer to hold data.
Publications & Further Reading
The following is a curated list of papers, articles, and resources related to Powersort, and its implementations in various frameworks.
Generic Info and Background on Powersort
Original publication first introducing Powersort.
Algorithmic Variants of Powersort
Multiway-merging variant of Powersort, effective in reducing memory traffic. Use for heavy objects.
Low-memory variant of Powersort that uses a simulated virtual memory to reduce merge buffer size to roughly O(√n), at very modest overhead. Use if memory is tight.
Enrichment & Fun
On this page, I collect basic information and links about Powersort. I've been doing this on my personal website, but felt it's time to move it to a dedicated site to make it easier to find and explore, and let other contribute to it.