Awesome
Intro
This document describes a stable bottom-up adaptive merge sort named twinsort.
The primary goal is to provide a simplified alternative to quadsort.
The algorithm is about 150 lines of sparse C code that should be relatively easy to understand and port.
Twin swap
The twin_swap function is a pre-sorting routine which turns the array into sorted blocks
of 2 elements. 8 9 4 1 2 3 6 0 5 7
becomes 8 9 1 4 2 3 0 6 5 7
.
The twin swap routine also contains a reverse order run detector, so 6 5 4 3 2 1
is sorted
into 1 2 3 4 5 6
rather than 5 6 3 4 1 2
.
The swap and the run detection are carried out simultaneously, subsequently the reverse run detector has very little overhead.
Tail sort
The tail_sort function is a bottom-up merge sort which uses at most n / 2 swap memory.
It merges by copying the right block to swap memory and merging the tails of each block. The routine skips unnecessary merge operations and boundary checks.
The tail merge routine also contains a forward run detector with minimal overhead.
Performance
Overall twinsort is slightly faster than Timsort, most notably on random data.
Big O
Name | Best | Average | Worst | Memory | Stable | Partitioning |
---|---|---|---|---|---|---|
twinsort | n | n log n | n log n | n | yes | no |
Twinsort makes n comparisons when the data is already sorted or reverse sorted.
Data types
Twinsort only supports integers, this to keep things simple.
Visualization
In the visualization below eight sorts are performed on various distributions. These distributions are: Random, Ascending, Ascending Saw, Generic, Descending, Descending Saw, Random Tail, and Wave order.
The upper half shows the swap memory and the bottom half shows the main memory. Colors are used to differentiate between skip, swap, and merge operations.
Benchmarks
The following benchmark is based on the wolfsort benchmark on WSL gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1).
The source code was compiled using g++ -O3 -w -fpermissive bench.c. Each test was ran 100 times and both the best and average run time is reported. stablesort is c++'s std::stablesort.
Name | Items | Type | Best | Average | Repetitions | Distribution |
---|---|---|---|---|---|---|
stablesort | 100000 | i32 | 0.006084 | 0.006174 | 1 | random order |
quadsort | 100000 | i32 | 0.005838 | 0.005920 | 1 | random order |
timsort | 100000 | i32 | 0.007556 | 0.007677 | 1 | random order |
twinsort | 100000 | i32 | 0.006265 | 0.006338 | 1 | random order |
stablesort | 100000 | i32 | 0.000658 | 0.000735 | 1 | ascending |
quadsort | 100000 | i32 | 0.000057 | 0.000057 | 1 | ascending |
timsort | 100000 | i32 | 0.000045 | 0.000045 | 1 | ascending |
twinsort | 100000 | i32 | 0.000126 | 0.000130 | 1 | ascending |
stablesort | 100000 | i32 | 0.001360 | 0.001467 | 1 | ascending saw |
quadsort | 100000 | i32 | 0.000852 | 0.000874 | 1 | ascending saw |
timsort | 100000 | i32 | 0.000851 | 0.000874 | 1 | ascending saw |
twinsort | 100000 | i32 | 0.000883 | 0.000909 | 1 | ascending saw |
stablesort | 100000 | i32 | 0.003891 | 0.003971 | 1 | generic order |
quadsort | 100000 | i32 | 0.003763 | 0.003855 | 1 | generic order |
timsort | 100000 | i32 | 0.005463 | 0.005569 | 1 | generic order |
twinsort | 100000 | i32 | 0.004090 | 0.004154 | 1 | generic order |
stablesort | 100000 | i32 | 0.000904 | 0.000941 | 1 | descending order |
quadsort | 100000 | i32 | 0.000066 | 0.000067 | 1 | descending order |
timsort | 100000 | i32 | 0.000101 | 0.000103 | 1 | descending order |
twinsort | 100000 | i32 | 0.000073 | 0.000074 | 1 | descending order |
stablesort | 100000 | i32 | 0.001054 | 0.001095 | 1 | descending saw |
quadsort | 100000 | i32 | 0.000488 | 0.000513 | 1 | descending saw |
timsort | 100000 | i32 | 0.000464 | 0.000478 | 1 | descending saw |
twinsort | 100000 | i32 | 0.000415 | 0.000427 | 1 | descending saw |
stablesort | 100000 | i32 | 0.002040 | 0.002139 | 1 | random tail |
quadsort | 100000 | i32 | 0.001557 | 0.001588 | 1 | random tail |
timsort | 100000 | i32 | 0.001979 | 0.002034 | 1 | random tail |
twinsort | 100000 | i32 | 0.001730 | 0.001764 | 1 | random tail |
stablesort | 100000 | i32 | 0.000978 | 0.001020 | 1 | wave order |
quadsort | 100000 | i32 | 0.000837 | 0.000910 | 1 | wave order |
timsort | 100000 | i32 | 0.000834 | 0.001081 | 1 | wave order |
twinsort | 100000 | i32 | 0.000947 | 0.000996 | 1 | wave order |