Performance¶
BielSort targets a specific workload: large Python lists containing exact signed 64-bit integers. Performance outside that scope is intentionally left to Timsort.
Measurements, not guarantees
The results below describe one development machine and fixed input distributions. CPU, Python build, allocator state, input shape, and system load can all change the outcome. Benchmark your real workload before making an engineering decision.
One-million-element snapshot¶
Median of five executions on the original Linux development machine. Times are
in seconds; a speedup above 1.00× favors BielSort.
New-list operation¶
This compares sorted(data) with bielsort.sort(data).
| Input | sorted() (s) |
BielSort (s) | Speedup |
|---|---|---|---|
| Dense range | 0.20259 | 0.04713 | 4.30× |
| Random int32 | 0.24079 | 0.05009 | 4.81× |
| Random int64 | 0.26314 | 0.07535 | 3.49× |
| 1024-bit integers | 0.33237 | 0.33417 | 0.99× |
| Nearly sorted | 0.01691 | 0.01730 | 0.98× |
In-place operation¶
This compares data.sort() with bielsort.sort_in_place(data).
| Input | list.sort() (s) |
BielSort (s) | Speedup |
|---|---|---|---|
| Dense range | 0.18953 | 0.03203 | 5.92× |
| Random int32 | 0.23666 | 0.03796 | 6.23× |
| Random int64 | 0.26081 | 0.06028 | 4.33× |
| 1024-bit integers | 0.30962 | 0.31676 | 0.98× |
| Nearly sorted | 0.01139 | 0.01097 | 1.04× |
Splitting the operations matters: sorted() and bielsort.sort() allocate a
new list, while list.sort() and bielsort.sort_in_place() mutate an existing
list. Input copies for in-place benchmarks are created before timing.
Reading the result honestly¶
Favorable¶
Large, unsorted, exact int64 lists can reach the native Counting or Radix path and showed substantial gains on this machine.
Neutral¶
Arbitrary-size integers use Timsort, so BielSort should be close to the built-in operation rather than faster.
Built-in advantage¶
Small and nearly sorted inputs already match Timsort's strengths. The selector falls back because native buffers would not be worthwhile.
Memory tradeoff¶
Native speed requires auxiliary buffers proportional to input size. An isolated one-million-element measurement found substantially higher peak memory than Python's built-in sort on favorable integer distributions.
A later Counting Sort buffer optimization produced these focused results:
| API | Baseline peak | Optimized peak | Reduction |
|---|---|---|---|
| New list | 41.99 MiB | 26.73 MiB | 36.3% |
| In place | 34.39 MiB | 19.05 MiB | 44.6% |
The optimization also preserved stable ordering and passed regular, stress, AddressSanitizer, and UndefinedBehaviorSanitizer tests. Memory remains a real tradeoff, not a solved cost.
NumPy comparison¶
NumPy has two different scenarios:
- end to end: convert
list[int]to an array, sort, and convert back; - array native: begin and end with an existing
numpy.ndarray.
At one million elements on the recorded machine:
| Input | BielSort (s) | NumPy end to end (s) | NumPy array (s) |
|---|---|---|---|
| Dense range | 0.05257 | 0.09432 | 0.05881 |
| Random int64 | 0.07975 | 0.11835 | 0.06677 |
BielSort won the equivalent end-to-end list operation in these measurements. When the input was already an array, NumPy was faster for the int64 case. These APIs should not be presented as interchangeable.
Reproduce the benchmarks¶
Clone the repository, create an environment, and install the editable project:
Run the timing, memory, and NumPy comparisons:
python benchmarks/benchmark.py -n 10000 100000 1000000 -r 5
python benchmarks/memory.py -n 1000000 -r 3
python benchmarks/numpy_comparison.py -n 10000 100000 1000000 -r 5
To evaluate transparent workload proxies and save a report that can be shared in a GitHub issue, run:
python benchmarks/workload_validation.py \
-n 10000 100000 1000000 \
-r 7 \
--json bielsort-workload-report.json
On Windows, activate .venv\Scripts\Activate.ps1. The isolated peak-memory
benchmark currently supports Linux and macOS.
Continue with the use-case and adoption guide before deciding whether an isolated speedup is meaningful to an application.
Recorded methodology¶
The repository retains the exact environment, commands, and longer tables: