Timsort

Fractional CTO · Founder of Injuria.ai
Timsort is a hybrid, stable sorting algorithm derived from merge sort[1] and insertion sort[2], designed to perform efficiently on many kinds of real-world data. It operates by finding subsequences of data that are already ordered, known as runs, and using them to sort the remainder of the dataset more efficiently.[30] By capitalizing on the existing order within the data, Timsort achieves a best-case time complexity of O(n) while maintaining a worst-case and average-case time complexity of O(n log n) Timsort Wikipedia[3].[31] Its space complexity is O(n) in the worst case, though it often requires much less memory in practice.
Before Timsort, standard libraries typically relied on general-purpose algorithms like Quicksort or standard merge sort. These algorithms were mathematically sound but often ignored the practical realities of the data they were processing. Timsort represented a paradigm shift in standard library design, moving away from pure theoretical elegance toward pragmatic, data-aware engineering. Tim Peters implemented Timsort in 2002 for use in the Python programming language, replacing the previous samplesort implementation[4]. The algorithm was specifically engineered to handle the partially sorted arrays frequently encountered in practical applications, rather than optimizing solely for random data. To achieve this, it employs a combination of a modified binary insertion sort for small segments and a sophisticated merge strategy for larger segments. The merge strategy includes a galloping mode, which uses exponential search to quickly skip over elements, significantly reducing the number of comparisons required when one run contains many consecutive elements that are smaller or larger than those in another run Real Python[5].
Since its introduction, Timsort has become one of the most widely used sorting algorithms[6] in modern software. It was the standard sorting algorithm in Python from version 2.3 until version 3.10, after which its merge policy was updated to a variant called Powersort in Python 3.11 Python 3.11 Release Notes[7]. Timsort was also adopted as the default sorting algorithm for non-primitive arrays in Java SE 7 Java Arrays.sort[8], the Android platform Android TimSort[9], the V8 JavaScript engine V8 Blog[10], and the Swift programming language Swift Sort[11]. Its widespread adoption highlights its practical efficiency, though its complex merge invariants also led to a subtle bug that remained undetected for over a decade until it was discovered through formal verification in 2015 Envisage Project[12].
The Problem of Real-World Data
Sorting algorithms are traditionally evaluated based on their performance on random permutations of data. Algorithms like Quicksort[13] and Heapsort[14] offer excellent average-case time complexities of O(n log n) (Quicksort has a worst-case time complexity of O(n^2)) and operate in-place, requiring minimal extra memory. However, data processed in real-world software applications is rarely entirely random. It frequently contains contiguous segments of elements that are already sorted, either in ascending or descending order. Traditional algorithms often fail to exploit this pre-existing order, performing redundant comparisons and memory swaps.
Timsort was created to solve the problem of sorting partially ordered data efficiently without sacrificing worst-case performance on random data. It is an adaptive sorting algorithm, meaning its execution time decreases as the amount of pre-existing order in the input increases.[32] In information theory, the entropy of a dataset measures its unpredictability. A completely random array has maximum entropy, requiring a maximum number of comparisons to sort. Conversely, an array that is already sorted has zero entropy. Traditional algorithms like Quicksort perform the same number of comparisons regardless of the entropy of the input. Timsort is designed to consume this entropy efficiently. By identifying existing runs, it avoids unnecessary work, effectively reducing the time complexity to O(n) for data with low entropy.
Timsort is also a stable sort. Stability guarantees that two elements with equal sorting keys will retain their original relative order in the sorted output. This property is essential when sorting complex objects by multiple criteria. Consider a database of employees. If the database is first sorted by alphabetical name, and then sorted by department, a stable sort guarantees that within each department, the employees will remain in alphabetical order. If an unstable sort were used for the second operation, the alphabetical ordering within the departments would be destroyed, requiring a much more complex composite sorting key to achieve the same result. Quicksort and Heapsort are inherently unstable, which limits their utility in these scenarios. By combining the stability of merge sort with the low overhead of insertion sort, Timsort provides a general-purpose sorting utility that adapts to the data it processes Baeldung[15].
Furthermore, Timsort is highly optimized for modern computer hardware. Modern central processing units execute instructions in a pipeline, often guessing the outcome of a conditional branch before it is evaluated. If the guess is wrong, the pipeline is flushed, wasting valuable clock cycles. Algorithms like Quicksort, which partition data based on a pivot, generate highly unpredictable branches when processing random or semi-random data. Timsort, by contrast, processes data in contiguous, predictable runs. The comparisons made during its merge phase are highly predictable, allowing the processor branch predictor to maintain a high success rate. Additionally, the linear memory access patterns of Timsort ensure that data is prefetched into the L1 and L2 caches of the processor, minimizing cache misses and memory latency.
Algorithmic Mechanism: Run Identification and Reversal
Timsort achieves its efficiency through a multi-step process that identifies, normalizes, and merges ordered segments of data. The algorithm avoids recursive function calls, relying instead on an iterative approach managed by a stack of pending operations.
The algorithm begins by scanning the input array from left to right to identify natural runs. A natural run is a contiguous sequence of elements that is either strictly descending or non-descending. The distinction between strictly descending and non-descending is necessary to maintain the stability of the sort. If a sequence contains equal elements, reversing it would alter their relative order, violating stability. Therefore, only strictly descending runs are identified as such.
When Timsort identifies a strictly descending run, it immediately reverses the elements in place to create a non-descending run. This in-place reversal is highly efficient. The algorithm uses two memory pointers, one at the beginning of the run and one at the end. It swaps the elements at these pointers and moves the pointers inward until they meet in the middle. This operation requires no additional memory allocation and executes in linear time relative to the length of the run. By normalizing all runs to be non-descending, the algorithm simplifies the subsequent merging phases, as it only needs to implement logic for merging ascending sequences.
Algorithmic Mechanism: Minrun Calculation and Binary Insertion Sort
To ensure that the merging phase operates efficiently, Timsort enforces a minimum length for all runs, known as the minrun. Merging is most efficient when the sizes of the runs being merged are roughly equal, and when the total number of runs is a power of two. If the runs are too short, the overhead of the merge logic outweighs the benefits.
The minrun value is calculated dynamically based on the total number of elements in the array, denoted as N. If N is less than 64, minrun is set to N, and Timsort simply uses binary insertion sort on the entire array. For arrays of 64 elements or larger, Timsort selects a minrun value between 32 and 64 inclusive. The upper bound of 64 elements is not arbitrary. It is closely tied to the hardware architecture of modern computers, specifically the size of the L1 cache. A standard cache line is typically 64 bytes, which cannot fit 64 pointers (512 bytes) or 64 primitive integers (256 bytes). Furthermore, 64 is the empirical threshold where the quadratic cost of shifting memory in binary insertion sort begins to outweigh the logarithmic efficiency of merge sort.
The calculation is performed using bitwise operations. It takes the most significant six bits of N, and if any of the remaining lower-order bits are set to 1, it adds 1 to the result. For example, if an array has 211 elements, the binary representation is 11010011. The first six bits represent the number 52. Because the remaining bits contain at least one 1, the algorithm adds 1, resulting in a minrun of 53. Dividing 211 by 53 yields approximately 3.98, which is very close to 4, a perfect power of two. This calculation ensures that the final merge operations are perfectly balanced.
When Timsort identifies a natural run that is shorter than the calculated minrun, it artificially extends the run to the required length. It does this by absorbing the subsequent elements in the array and sorting them into the run using binary insertion sort.
Binary insertion sort is an optimization of the standard insertion sort algorithm. Instead of scanning backward linearly to find the correct insertion point for a new element, it uses binary search. This reduces the number of comparisons required for each insertion from O(n) to O(log n). For a run bounded by the 64-element maximum minrun, binary search finds the correct insertion point in at most six comparisons. While the algorithm still requires memory shifts to make room for the inserted element, the constant factors associated with moving small blocks of memory in modern processors are very low. For small arrays, binary insertion sort is exceptionally fast and outperforms more complex algorithms.
Algorithmic Mechanism: The Merge Stack and Invariants
As Timsort processes the array and normalizes runs to the minrun length, it pushes the starting index and length of each run onto a stack. To minimize memory usage and keep the stack size small, Timsort opportunistically merges runs while the array is still being scanned. The decision of when to merge is governed by a set of invariants applied to the top elements of the stack.
Let the lengths of the three most recently added runs on the stack be A, B, and C, where A is the run at the top of the stack representing the most recent run, B is the second, and C is the third. Timsort requires the following invariants to hold true:
- A + B < C
- A < B
The stack invariants are checked continuously. When a new run is pushed onto the stack, Timsort evaluates the top three runs. If either of these invariants is violated, Timsort initiates a merge. It compares the lengths of A and C. If A is smaller than C, it merges A and B. If C is smaller, it merges B and C. This process repeats until the invariants are restored.
while len(stack) >= 3:
A = stack[-1]
B = stack[-2]
C = stack[-3]
if C <= A + B or B <= A:
if C < A:
merge_runs(stack[-3], stack[-2])
else:
merge_runs(stack[-2], stack[-1])
else:
break
This specific merging strategy ensures that runs of roughly equal size are merged together, which is the optimal scenario for a merge sort. It always chooses to merge the two smaller adjacent runs. This minimizes the size of the temporary memory required for the merge and ensures that the resulting merged run is as balanced as possible before it is compared against the next run on the stack. It also guarantees that the sizes of the runs on the stack grow exponentially. Because the run lengths grow at an exponential rate, the maximum size of the stack is bounded to a small logarithmic number. This prevents excessive memory allocation and ensures that the algorithm can handle arrays of any size with a fixed, small amount of stack memory.
Algorithmic Mechanism: Galloping Mode
The actual merging of two adjacent runs is performed using temporary memory. Timsort allocates a temporary array equal in size to the smaller of the two runs being merged. It copies the smaller run into the temporary array and then merges the elements back into the main array by comparing the temporary elements with the elements of the larger run.
To optimize this process, Timsort employs a technique called galloping. During a standard merge, the algorithm compares the next element of the left run with the next element of the right run and moves the smaller one into place. Timsort tracks how many consecutive elements are selected from the same run. If this count reaches a specific threshold, called min_gallop, which is initially set to 7, the algorithm assumes that many more elements from that same run will continue to win the comparison.
At this point, Timsort switches to galloping mode. Instead of checking elements one by one, it uses exponential search. It compares the target element against elements at indices 1, 3, 7, 15, and so on, doubling the offset each time until it finds an element larger than the target. This bounds the location of the target to a specific interval. Once the exponential search bounds the target element between two indices, Timsort performs a standard binary search within that specific window. This combination of exponential search followed by binary search guarantees that the algorithm finds the insertion point in O(log k) comparisons, where k is the length of the run. After the exact insertion point is identified, Timsort uses a bulk memory copy function, such as memmove in the C programming language, to shift the entire block of elements simultaneously.
For example, consider a scenario where Run A contains the numbers 1 through 8, and Run B contains the numbers 9 through 11. In a standard linear merge, the algorithm would compare the number 1 with the number 9, select 1, and then proceed to compare 2 with 9, 3 with 9, and so on. This would require eight individual comparisons to place all the elements of Run A. In galloping mode, after seven consecutive wins for Run A, the algorithm assumes a trend. It takes the next element of Run B, which is 9, and searches for it within Run A using exponential offsets. It checks index 1, then index 3, then index 7. Once it determines that 9 is greater than the element at index 7, it performs a binary search in the remaining portion of the run. It quickly determines that 9 belongs at the very end of Run A, allowing it to copy the entire remaining block of Run A in a single operation.
Galloping is highly effective when the data contains long sequences of elements from one run that belong before the elements of the other run. To prevent galloping from slowing down the algorithm on random data, Timsort adjusts the min_gallop threshold dynamically. If galloping successfully skips many elements, the threshold is decreased, making it easier to enter galloping mode again. If galloping skips very few elements, the threshold is increased, penalizing the mode and returning the algorithm to standard linear merging.
Discovery and History
Timsort was invented by software engineer Tim Peters in 2002. Peters, known in the Python community for his prolific contributions and his authorship of "The Zen of Python", approached the sorting problem with a focus on real-world benchmarking. He recognized that Python lists were frequently appended to, concatenated, and modified in ways that naturally produced ordered segments. He designed the algorithm specifically for the Python programming language, seeking a replacement for the existing samplesort implementation used by the list sorting method. He introduced the algorithm to the Python developer community via the python-dev mailing list Tim Peters Python-Dev 2002[16], documenting the design, the rationale behind the heuristics, and the exact mechanics in a detailed text file named listsort.txt, which was committed to the Python source code repository.
The algorithm was not created in a vacuum. It synthesized several existing concepts in computer science. The galloping technique was heavily influenced by the 1990 paper "Sublinear merging and natural merge sort" by Svante Carlsson, Christos Levcopoulos, and Ola Petersson Carlsson et al. 1990[17], as well as the 1993 paper "Optimistic Sorting and Information Theoretic Complexity" by Peter McIlroy McIlroy 1993[18]. Peters combined these theoretical concepts with intense practical engineering, tuning the constants and thresholds based on empirical performance across a wide variety of real-world datasets. The initial min_gallop value of 7, for instance, was chosen because empirical testing showed it provided the best balance between rewarding long runs and penalizing random data.
Timsort was integrated into Python version 2.3 in 2003. Its success in Python caught the attention of other language designers. In 2009, software engineer Joshua Bloch ported Timsort to the Java programming language. It was introduced in Java SE 7 as the default sorting algorithm for arrays of objects, replacing the modified merge sort that had been used previously. This port was subsequently adopted by the Android Open Source Project, bringing Timsort to billions of mobile devices worldwide.
Formal Verification and the 2015 Bug
For over a decade, Timsort operated in production environments across multiple languages without any reported algorithmic failures. However, in 2015, a team of computer scientists including Stijn de Gouw, Jurriaan Rot, Frank S. de Boer, Richard Bubel, and Reiner Hähnle discovered a critical flaw in the algorithm logic.[33]
The researchers were attempting to formally verify the correctness of the Java implementation of Timsort using a deductive verification tool called KeY KeY Project[19].[34] Formal verification involves mathematically proving that a program adheres to its specifications under all possible conditions. Deductive verification tools like KeY work by translating the source code of a program into a series of mathematical formulas. These formulas are then evaluated by an automated theorem prover to ensure that they hold true under all possible input states. The researchers used KeY to model the exact memory state of the Java virtual machine while executing Timsort. During this process, they found that the stack invariants enforced by Timsort were insufficient to guarantee that the exponential growth property held true for all elements deep within the stack Auger et al. 2018[20].
Because the invariants only checked the top three runs, a highly specific sequence of run lengths could cause the stack to collapse in a way that left smaller runs below larger runs. The researchers found that to break the invariants, an attacker would need to construct an array that produced a sequence of run lengths that did not grow exponentially but just barely satisfied the three-element invariant checks.[35] If an array was carefully crafted to produce this exact sequence of descending run lengths, the number of pending runs on the stack would grow linearly rather than logarithmically. The Java implementation pre-allocated the run stack to a maximum of 40 (later 49) entries, whereas 85 was used in the Python C implementation, which was mathematically proven to be sufficient for arrays up to 2^64 elements if the invariants held globally. Because the invariants failed, an array of approximately 67 million elements could cause the stack to exceed 40 entries, resulting in an ArrayIndexOutOfBoundsException and crashing the application.[34]
The researchers disclosed the vulnerability to the maintainers of Java, Python, and Android. The bug was patched by modifying the invariant check to examine the top four runs on the stack instead of just the top three. By ensuring that A + B < C, A < B, and additionally B + C < D and B < C, the exponential growth property was strictly maintained across the entire stack OpenJDK Bug JDK-8072909[21]. This event highlighted the value of formal methods in identifying edge-case vulnerabilities in widely deployed, heavily tested software Python Issue 23515[22].
Refinements and Variants
While the 2015 bug fix resolved the stack overflow issue, algorithmic researchers continued to analyze the merge strategy of Timsort. In 2018, computer scientists J. Ian Munro and Sebastian Wild published a paper titled "Nearly-Optimal Mergesorts: Fast, Practical Sorting Methods That Optimally Adapt to Existing Runs" Munro and Wild 2018[23]. They demonstrated that the heuristic used by Timsort for choosing which runs to merge could result in a merge tree that was up to 50 percent more costly than the mathematical optimum Dagstuhl PDF[24].
To address this, Munro and Wild introduced a variant called Powersort Powersort Wikipedia[25]. Powersort abandons the stack invariants of Timsort entirely. Instead, it calculates the power of the boundary between two adjacent runs. In Powersort, a conceptual binary tree is constructed such that the root node represents the midpoint of the entire array. The left child represents the midpoint of the left half, and the right child represents the midpoint of the right half. When two adjacent runs are identified, Powersort calculates the lowest common ancestor of their midpoints in this tree. The depth of this ancestor node becomes the power of the boundary between the runs. Merging runs with higher boundary powers first ensures that the overall merge tree remains perfectly balanced, regardless of the initial sizes of the natural runs. This guarantees a near-optimal merge tree and mathematically prevents the stack overflow issues associated with the original heuristics of Timsort Wild PyCon Talk[26].
Due to its provably superior worst-case merge costs and simplified logic, Powersort was adopted as the replacement for the merge policy of Timsort in Python version 3.11, released in 2022 Python Issue 34561[27]. Other variants, such as Shiversort and Peeksort, have also been proposed in academic literature, offering different tradeoffs in how they identify runs and construct merge trees.
Real-World Applications
Timsort and its direct derivatives are embedded in the standard libraries of many major programming languages and platforms.
In Python, Timsort powered the sorted built-in function and the list sorting method from version 2.3 until version 3.10. Even with the transition to Powersort in Python 3.11, the core run identification, binary insertion, and galloping mechanics of Timsort remain intact.
In the Java ecosystem, Timsort is the underlying algorithm for array sorting when dealing with arrays of objects. Java continues to use Dual-Pivot Quicksort for arrays of primitive types, such as integers and floating-point numbers, because primitives do not require a stable sort, and Quicksort operates entirely in-place without the memory overhead of Timsort.
The V8 JavaScript engine, which powers Google Chrome and Node.js, adopted Timsort in 2018 with the release of V8 version 7.0. The ECMAScript 2019 language specification mandated that the array sorting prototype must be a stable sort ECMAScript 2019[28]. V8 had previously used an unstable Quicksort implementation for large arrays. To comply with the new standard without sacrificing performance on partially sorted data, the V8 engineering team implemented Timsort. The implementation required careful engineering to ensure that the transition to Timsort did not introduce performance regressions for web applications that relied on the speed of the previous Quicksort implementation. By fine-tuning the minrun calculation and the galloping thresholds specifically for JavaScript arrays, the V8 team successfully deployed Timsort to billions of Chrome browser instances.
Other notable adoptions include the Swift programming language standard library and the GNU Octave scientific programming language. The Rust standard library utilized a modified version of Timsort for its stable slice sorting method until version 1.81, released in 2024, when it transitioned to driftsort, a modern algorithm that builds upon the run detection principles of Timsort while optimizing for superscalar processor architectures Rust slice::sort[29].
Significance and Tradeoffs
The primary significance of Timsort is its recognition that theoretical worst-case time complexity on random data is not the only metric that matters in software engineering. By optimizing for the partially sorted data that naturally occurs in databases, user inputs, and sequential logs, Timsort drastically reduces processor cycles and power consumption in everyday computing tasks.[32]
However, the algorithm involves specific tradeoffs. Timsort is not an in-place sorting algorithm. In the worst case, it requires temporary memory equal to half the size of the array being sorted, resulting in an O(n) space complexity. Compared to a standard merge sort, which also requires O(n) auxiliary space, Timsort is vastly superior because it minimizes the use of this temporary memory. Standard merge sort allocates temporary arrays for every merge operation, regardless of the data structure. Timsort only allocates temporary memory equal to the size of the smaller run being merged, and it reuses this memory across multiple operations, significantly reducing the burden on the garbage collector in managed languages like Java and Python. While this is generally acceptable on modern desktop and server hardware, it makes Timsort unsuitable for embedded systems or microcontrollers with strict memory constraints.
Additionally, the complexity of the logic introduces overhead. For very small arrays, or arrays containing purely random data with no natural runs, the constant factors involved in calculating minrun, managing the stack, and checking galloping thresholds make Timsort slightly slower than a highly optimized, non-adaptive Quicksort. Despite these tradeoffs, its stability and exceptional performance on real-world data have cemented its status as a foundational algorithm in modern computer science.
Sources (35)
- Wikipedia: merge sort
- Wikipedia: insertion sort
- Wikipedia: Timsort Wikipedia
- listsort.txt
- Real Python
- Wikipedia: sorting algorithms
- Python 3.11 Release Notes
- Java Arrays.sort
- Android TimSort
- V8 Blog
- Swift Sort
- Envisage Project
- Wikipedia: Quicksort
- Wikipedia: Heapsort
- Baeldung
- Tim Peters Python-Dev 2002
- Carlsson et al. 1990
- ACM Digital Library
- KeY Project
- Auger et al. 2018
- OpenJDK Bug JDK-8072909
- Python Issue 23515
- Munro and Wild 2018
- Dagstuhl PDF
- Wikipedia: Powersort Wikipedia
- Wild PyCon Talk
- Python Issue 34561
- ECMAScript 2019
- Rust slice::sort
- Steven S. Skiena, The Algorithm Design Manual (Springer Nature, 2020)
- Mufti Mahmud, Maryam Doborjeh, Kevin Wong, Neural Information Processing (Springer Nature, 2025)
- Mikhail Davidovich, Practical Data Structures and Algorithms for Java Developers (Springer Nature, 2026)
- Marieke Huisman, Corina Păsăreanu, Naijun Zhan, Formal Methods (Springer Nature, 2021)
- Brijesh Dongol, Elena Troubitsyna, Integrated Formal Methods (Springer Nature, 2020)
- Daniel Kroening, Corina S. Păsăreanu, Computer Aided Verification (Springer, 2015)