Skip to content

Commit aeb535f

Browse files
authored
Merge pull request #209 from Morwenn/develop
Release 1.13.1
2 parents b3ce19e + b0de0fa commit aeb535f

File tree

106 files changed

+3557
-3004
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+3557
-3004
lines changed

.github/workflows/build-macos.yml

+4-6
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ on:
2323

2424
jobs:
2525
build:
26-
runs-on: macos-10.15
26+
runs-on: macos-11
2727

2828
strategy:
2929
fail-fast: false
3030
matrix:
3131
cxx:
32-
- g++-9
33-
- $(brew --prefix llvm)/bin/clang++ # Clang 11
32+
- g++-10
33+
- clang++
3434
config:
3535
# Release build
3636
- build_type: Release
@@ -41,8 +41,7 @@ jobs:
4141
sanitize: undefined
4242

4343
steps:
44-
- uses: actions/checkout@v2
45-
- uses: seanmiddleditch/gha-setup-ninja@master
44+
- uses: actions/checkout@v3
4645

4746
- name: Configure CMake
4847
working-directory: ${{runner.workspace}}
@@ -52,7 +51,6 @@ jobs:
5251
-DCMAKE_CONFIGURATION_TYPES=${{matrix.config.build_type}} \
5352
-DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} \
5453
-DCPPSORT_SANITIZE=${{matrix.config.sanitize}} \
55-
-GNinja \
5654
-DCPPSORT_BUILD_EXAMPLES=ON
5755
5856
- name: Build the test suite

.github/workflows/build-mingw.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
build_type: [Debug, Release]
3232

3333
steps:
34-
- uses: actions/checkout@v2
34+
- uses: actions/checkout@v3
3535

3636
- name: Configure CMake
3737
shell: pwsh

.github/workflows/build-msvc.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
build_type: [Debug, Release]
3232

3333
steps:
34-
- uses: actions/checkout@v2
34+
- uses: actions/checkout@v3
3535

3636
- name: Configure CMake
3737
shell: pwsh

.github/workflows/build-ubuntu.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
sanitize: undefined
4444

4545
steps:
46-
- uses: actions/checkout@v2
46+
- uses: actions/checkout@v3
4747

4848
- name: Install GCC
4949
if: ${{matrix.cxx == 'g++-5'}}

.github/workflows/code-coverage.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323

2424
steps:
2525
- name: Checkout project
26-
uses: actions/checkout@v2
26+
uses: actions/checkout@v3
2727

2828
- name: Install LCOV
2929
run: sudo apt-get install -y lcov

.github/workflows/deploy-to-wiki.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ jobs:
1818

1919
steps:
2020
- name: Checkout /docs
21-
uses: actions/checkout@v2
21+
uses: actions/checkout@v3
2222
with:
2323
repository: ${{github.repository}}
2424
path: main
2525

2626
- name: Checkout wiki
27-
uses: actions/checkout@v2
27+
uses: actions/checkout@v3
2828
with:
2929
repository: ${{github.repository}}.wiki
3030
path: wiki

.gitignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# Copyright (c) 2015-2021 Morwenn
1+
# Copyright (c) 2015-2022 Morwenn
22
# SPDX-License-Identifier: MIT
33

44
# Usual build directory
5-
build
5+
build*
66

77
# Benchmark results directories
88
results

CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# Copyright (c) 2015-2021 Morwenn
1+
# Copyright (c) 2015-2022 Morwenn
22
# SPDX-License-Identifier: MIT
33

44
cmake_minimum_required(VERSION 3.8.0)
55

66
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
77

8-
project(cpp-sort VERSION 1.13.0 LANGUAGES CXX)
8+
project(cpp-sort VERSION 1.13.1 LANGUAGES CXX)
99

1010
include(CMakePackageConfigHelpers)
1111
include(GNUInstallDirs)

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
![cpp-sort logo](docs/images/cpp-sort-logo.svg)
22

3-
[![Latest Release](https://img.shields.io/badge/release-1.13.0-blue.svg)](https://github.com/Morwenn/cpp-sort/releases/tag/1.13.0)
4-
[![Conan Package](https://img.shields.io/badge/conan-cpp--sort%2F1.13.0-blue.svg)](https://conan.io/center/cpp-sort?version=1.13.0)
3+
[![Latest Release](https://img.shields.io/badge/release-1.13.1-blue.svg)](https://github.com/Morwenn/cpp-sort/releases/tag/1.13.1)
4+
[![Conan Package](https://img.shields.io/badge/conan-cpp--sort%2F1.13.1-blue.svg)](https://conan.io/center/cpp-sort?version=1.13.1)
55
[![Code Coverage](https://codecov.io/gh/Morwenn/cpp-sort/branch/develop/graph/badge.svg)](https://codecov.io/gh/Morwenn/cpp-sort)
66
[![Pitchfork Layout](https://img.shields.io/badge/standard-PFL-orange.svg)](https://github.com/vector-of-bool/pitchfork)
77

benchmarks/benchmarking-tools/distributions.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015-2021 Morwenn
2+
* Copyright (c) 2015-2022 Morwenn
33
* SPDX-License-Identifier: MIT
44
*/
55
#include <algorithm>
@@ -13,6 +13,7 @@
1313
#include <cpp-sort/detail/type_traits.h>
1414
#include <cpp-sort/utility/as_function.h>
1515
#include <cpp-sort/utility/functional.h>
16+
#include <cpp-sort/utility/functional.h>
1617

1718
// Pseudo-random number generator, used by some distributions
1819
thread_local std::mt19937_64 distributions_prng(std::time(nullptr));
@@ -400,9 +401,10 @@ namespace dist
400401
static constexpr const char* output = "vergesort_killer.txt";
401402
};
402403

403-
struct as_long_string
404+
struct as_long_string:
405+
cppsort::utility::projection_base
404406
{
405-
auto operator()(long long int value)
407+
auto operator()(long long int value) const
406408
-> std::string
407409
{
408410
auto str = std::to_string(value);

conanfile.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22

3-
# Copyright (c) 2018-2021 Morwenn
3+
# Copyright (c) 2018-2022 Morwenn
44
# SPDX-License-Identifier: MIT
55

66
from conans import CMake, ConanFile
@@ -10,7 +10,7 @@
1010

1111
class CppSortConan(ConanFile):
1212
name = "cpp-sort"
13-
version = "1.13.0"
13+
version = "1.13.1"
1414
description = "Additional sorting algorithms & related tools"
1515
topics = "conan", "cpp-sort", "sorting", "algorithms"
1616
url = "https://github.com/Morwenn/cpp-sort"

docs/Benchmarks.md

+20-17
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
*Note: this page only benchmarls sorting algorithms under specific conditions. It can be used as a quick guide but if you really need a fast algorithm for a specific use case, you better run your own benchmarks.*
1+
*Note: this page only benchmarks sorting algorithms under specific conditions. It can be used as a quick guide but if you really need a fast algorithm for a specific use case, you better run your own benchmarks.*
22

3-
*Last meaningful update: 1.9.0 release, 1.12.0 for measures of presortedness.*
3+
*Last meaningful updates:*
4+
* *1.13.1 for unstable random-access sorts, slow O(n log n) sorts, forward sorts, and the expensive move/cheap comparison benchmark*
5+
* *1.12.0 for measures of presortedness*
6+
* *1.9.0 otherwise*
47

58
Benchmarking is hard and I might not be doing it right. Moreover, benchmarking sorting algorithms highlights that the time needed to sort a collection of elements depends on several things: the type to sort, the size of the collection, the cost of comparing two values, the cost of moving an element, the patterns formed by the distribution of the values in the collection to sort, the type of the collection itself, etc. The aim of this page is to help you choose a sorting algorithm depending on your needs. You can find two main kinds of benchmarks: the ones that compare algorithms against shuffled collections of different sizes, and the ones that compare algorithms against different data patterns for a given collection size.
69

710
It is worth noting that most benchmarks on this page use collections of `double`: the idea is to sort collections of a simple enough type without getting numbers skewed by the impressive amount of optimizations that compilers are able to perform for integer types. While not perfect, `double` (without NaNs or infinities) falls into the "cheap enough to compare, cheap enough to move" category that most of the benchmarks here target.
811

912
All of the graphs on this page have been generated with slightly modified versions of the scripts found in the project's benchmarks folder. There are just too many things to check; if you ever want a specific benchmark, don't hesitate to ask for it.
1013

11-
*The benchmarks were run on Windows 10 with 64-bit MinGW-w64 g++10.1, with the flags -O3 -march=native -std=c++2a.*
14+
*The latest benchmarks were run on Windows 10 with 64-bit MinGW-w64 g++12.0, with the flags -O3 -march=native -std=c++20.*
1215

1316
# Random-access iterables
1417

@@ -19,7 +22,7 @@ Most sorting algorithms are designed to work with random-access iterators, so th
1922
Sorting a random-access collection with an unstable sort is probably one of the most common things to want, and not only are those sorts among the fastest comparison sorts, but type-specific sorters can also be used to sort a variety of types. If you don't know what algorithm you want and don't have specific needs, then you probably want one of these.
2023

2124
![Benchmark speed of unstable sorts with increasing size for std::vector<double>](https://i.imgur.com/Q3IEeci.png)
22-
![Benchmark speed of unstable sorts with increasing size for std::deque<double>](https://i.imgur.com/XjRGUmc.png)
25+
![Benchmark speed of unstable sorts with increasing size for std::deque<double>](https://i.imgur.com/oRW5kFr.png)
2326

2427
The plots above show a few general tendencies:
2528
* `selection_sort` is O(n²) and doesn't scale.
@@ -28,8 +31,8 @@ The plots above show a few general tendencies:
2831

2932
The quicksort derivatives and the hybrid radix sorts are generally the fastest of the lot, yet `drop_merge_sort` seems to offer interesting speedups for `std::deque` despite not being designed to be the fastest on truly shuffled data. Part of the explanation is that it uses `pdq_sort` in a contiguous memory buffer underneath, which might be faster for `std::deque` than sorting completely in-place.
3033

31-
![Benchmark unstable sorts over different patterns for std::vector<double>](https://i.imgur.com/MlEcGuL.png)
32-
![Benchmark unstable sorts over different patterns for std::deque<double>](https://i.imgur.com/o7sOfMB.png)
34+
![Benchmark unstable sorts over different patterns for std::vector<double>](https://i.imgur.com/WZ4s6Xt.png)
35+
![Benchmark unstable sorts over different patterns for std::deque<double>](https://i.imgur.com/UAaObUW.png)
3336

3437
A few random takeways:
3538
* All the algorithms are more or less adaptive, not always for the same patterns.
@@ -61,15 +64,15 @@ These plots highlight a few important things:
6164

6265
I decided to include a dedicated category for slow O(n log n) sorts, because I find this class of algorithms interesting. This category contains experimental algorithms, often taken from rather old research papers. `heap_sort` is used as the "fast" algorithm in this category, despite it being consistently the slowest in the previous category.
6366

64-
![Benchmark speed of slow O(n log n) sorts with increasing size for std::vector<double>](https://i.imgur.com/nSX9n1q.png)
65-
![Benchmark slow O(n log n) sorts over different patterns for std::vector<double>](https://i.imgur.com/z9dR16G.png)
66-
![Benchmark slow O(n log n) sorts over different patterns for std::deque<double>](https://i.imgur.com/Viu13nj.png)
67+
![Benchmark speed of slow O(n log n) sorts with increasing size for std::vector<double>](https://i.imgur.com/2sx8Hk7.png)
68+
![Benchmark slow O(n log n) sorts over different patterns for std::vector<double>](https://i.imgur.com/RkiYdy8.png)
69+
![Benchmark slow O(n log n) sorts over different patterns for std::deque<double>](https://i.imgur.com/Z9O4I6p.png)
6770

6871
The analysis is pretty simple here:
6972
* Most of the algorithms in this category are slow, but exhibit a good adaptiveness with most kinds of patterns. It isn't all that surprising since I specifically found them in literature about adaptive sorting.
70-
* `poplar_sort` is slower for `std::vector` than for `std::deque`, which makes me suspect a codegen issue somewhere.
73+
* `poplar_sort` is a bit slower for `std::vector` than for `std::deque`, which makes me suspect a weird issue somewhere.
7174
* As a result `smooth_sort` and `poplar_sort` beat each other depending on the type of the collection to sort.
72-
* Slabsort has an unusual graph: it seems that even for shuffled data it might end up beating `heap_sort` when the collection grows big enough.
75+
* Slabsort has an unusual graph: even for shuffled data it might end up beating `heap_sort` when the collection becomes big enough.
7376

7477
# Bidirectional iterables
7578

@@ -91,14 +94,14 @@ For elements as small as `double`, there are two clear winners here: `drop_merge
9194

9295
Even fewer sorters can handle forward iterators. `out_of_place_adapter(pdq_sort)` was not included in the patterns benchmark, because it adapts to patterns the same way `pdq_sort` does.
9396

94-
![Benchmark speed of sorts with increasing size for std::forward_list<double>](https://i.imgur.com/SMTKhqG.png)
95-
![Benchmark sorts over different patterns for std::forward_list<double>](https://i.imgur.com/XLndRbU.png)
97+
![Benchmark speed of sorts with increasing size for std::forward_list<double>](https://i.imgur.com/if15kX1.png)
98+
![Benchmark sorts over different patterns for std::forward_list<double>](https://i.imgur.com/uF0UzLm.png)
9699

97100
The results are roughly the same than with bidirectional iterables:
98101
* Sorting out-of-place is faster than anything else.
99-
* [`std::forward_list::sort`][std-forward-list-sort] doesn't scale well unless moves are expensive.
102+
* [`std::forward_list::sort`][std-forward-list-sort] doesn't scale well when moves are inexpensive.
100103
* `quick_sort` and `quick_merge_sort` are good enough contenders when trying to avoid heap memory allocations.
101-
* `mel_sort` is still bad, but becomes a dcent alternative when the input exhibits recognizable patterns.
104+
* `mel_sort` is still bad, but becomes a decent alternative when the input exhibits recognizable patterns.
102105

103106
# Sorting under specific constraints
104107

@@ -129,11 +132,11 @@ Both algorithms can be interesting depending on the sorting scenario.
129132

130133
## Expensive moves, cheap comparisons
131134

132-
Sometimes we have to sort a collection whose elements are expensive to move around but cheap to compare. In such a situation we can use `indirect_adapter` which sorts iterators to the elements and moves the elements into their direct place once the sorting order is known.
135+
Sometimes one has to sort a collection whose elements are expensive to move around but cheap to compare. In such a situation `indirect_adapter` can be used: it sorts a collection of iterators to the elements, and moves the elements into their direct place once the sorting order is known.
133136

134137
The following example uses a collection of `std::array<doube, 100>` whose first element is the only one compared during the sort. Albeit a bit artificial, it illustrates the point well enough.
135138

136-
![Benchmark heap_sort vs. indirect_adapter(heap_sort) for a collection of std::array<double, 100>](https://i.imgur.com/mYUaxRT.png)
139+
![Benchmark heap_sort vs. indirect_adapter(heap_sort) for a collection of std::array<double, 100>](https://i.imgur.com/Okkahwf.png)
137140

138141
The improvements are not always as clear as in this benchmark, but it shows that `indirect_adapter` might be an interesting tool to have in your sorting toolbox in such a scenario.
139142

docs/Chainable-projections.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
*New in version 1.7.0*
22

3-
Sometimes one might need to apply several transformations to the elements of a collection before comparing them. To support this use case, some projection functions in **cpp-sort** can be composed with `operator|`
3+
Sometimes one needs to apply several transformations to the elements of a collection before comparing them. To support this use case, some projection functions in **cpp-sort** can be composed with `operator|`
44

55
```cpp
66
struct my_negate:

docs/Changelog.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ When compiled with C++20, **cpp-sort** might gain a few additional features depe
7373

7474
* When available, [`std::ranges::less`][std-ranges-less] and [`std::ranges::greater`][std-ranges-greater] benefit from dedicated support wherever [`std::less<>`][std-less-void] and [`std::greater<>`][std-greater-void] are supported, with equivalent semantics.
7575

76-
* [`utility::iter_swap`][utility-iter-move] can now be used in more `constexpr` functions thanks to [`std::swap`][std-swap] begin `constexpr`.
76+
* [`utility::iter_swap`][utility-iter-move] can now be used in more `constexpr` functions thanks to [`std::swap`][std-swap] being `constexpr`.
7777

7878
The feature-test macro `__cpp_lib_constexpr_algorithms` can be used to check whether `std::swap` is `constexpr`.
7979

@@ -89,6 +89,8 @@ When compiled with C++20, **cpp-sort** might gain a few additional features depe
8989

9090
* Assumptions: some algorithms use assumptions in select places to make the compiler generate more efficient code. Whether such assumptions are available depends on the compiler.
9191

92+
* Vectorized algorithms: when compiled against the Microsoft STL, **cpp-sort** tries to take advantage of their vectorized algorithms when possible. This improves some algorithms when sorting contiguous collections of trivially copyable types.
93+
9294
* When using libstdc++, libc++ or the Microsoft STL, the return type of [`std::mem_fn`][std-mem-fn] is considered ["probably branchless"][branchless-traits] when it wraps a pointer to data member, which can improve the speed of [`pdq_sorter`][pdq-sorter] and everything that relies on it in some scenarios.
9395

9496

docs/Comparators.md

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Total order comparators are considered as [generating branchless code][branchles
2929

3030
*Changed in version 1.5.0:* `total_greater` and `total_less` are respectively of type `total_greater_t` and `total_less_t`.
3131

32+
*Changed in version 1.13.1:* support for `[un]signed __int128`.
33+
3234
### Weak order comparators
3335

3436
```cpp
@@ -51,6 +53,8 @@ Weak order comparators are considered as [generating branchless code][branchless
5153

5254
*Changed in version 1.5.0:* `weak_greater` and `weak_less` are respectively of type `weak_greater_t` and `weak_less_t`.
5355

56+
*Changed in version 1.13.1:* support for `[un]signed __int128`.
57+
5458
### Partial order comparators
5559

5660
```cpp
@@ -66,6 +70,8 @@ Partial order comparators are considered as [generating branchless code][branchl
6670

6771
*Changed in version 1.5.0:* `partial_greater` and `partial_less` are respectively of type `partial_greater_t` and `partial_less_t`.
6872

73+
*Changed in version 1.13.1:* support for `[un]signed __int128`.
74+
6975
### Natural order comparator
7076

7177
```cpp

docs/Fixed-size-sorters.md

+11-3
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,9 @@ template<typename DifferenceType=std::ptrdiff_t>
105105
-> std::array<utility::index_pair<DifferenceType>, /* Number of CEs in the network */>;
106106
```
107107

108-
### `merge_exchange_network_sorter`
108+
*New in version 1.11.0*
109+
110+
### `odd_even_merge_network_sorter`
109111

110112
```cpp
111113
#include <cpp-sort/fixed/odd_even_merge_network_sorter.h>
@@ -128,6 +130,8 @@ template<typename DifferenceType=std::ptrdiff_t>
128130
-> std::array<utility::index_pair<DifferenceType>, /* Number of CEs in the network */>;
129131
```
130132

133+
*New in version 1.11.0*
134+
131135
### `sorting_network_sorter`
132136

133137
```cpp
@@ -147,7 +151,7 @@ Size | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16
147151
:-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-:
148152
**CEs** | 0 | 1 | 3 | 5 | 9 | 12 | 16 | 19 | 25 | 29 | 35 | 39 | 45 | 51 | 56 | 60
149153
**Size** | **17** | **18** | **19** | **20** | **21** | **22** | **23** | **24** | **25** | **26** | **27** | **28** | **29** | **30** | **31** | **32**
150-
**CEs** | 71 | 77 | 85 | 91 | 99 | 106 | 114 | 120 | 131 | 139 | 148 | 155 | 164 | 172 | 180 | 185
154+
**CEs** | 71 | 77 | 85 | 91 | 99 | 106 | 114 | 120 | 130 | 139 | 148 | 155 | 164 | 172 | 180 | 185
151155

152156
One of the main advantages of sorting networks is the fixed number of CEs required to sort a collection: this means that sorting networks are far more resistant to time and cache attacks since the number of performed comparisons does not depend on the contents of the collection. However, additional care (not provided by the library) is required to ensure that the algorithms always perform the same amount of memory loads and stores. For example, one could create a `constant_time_iterator` with a dedicated `iter_swap` tuned to perform a constant-time compare-exchange operation.
153157

@@ -157,7 +161,7 @@ All specializations of `sorting_network_sorter` provide a `index_pairs() static`
157161

158162
```cpp
159163
template<typename DifferenceType=std::ptrdiff_t>
160-
static constexpr auto index_pairs()
164+
[[nodiscard]] static constexpr auto index_pairs()
161165
-> std::array<utility::index_pair<DifferenceType>, /* Number of CEs in the network */>;
162166
```
163167

@@ -171,6 +175,10 @@ static constexpr auto index_pairs()
171175

172176
*Changed in version 1.13.0:* sorting 21, 22, 23, 25, 27 and 29 inputs respectively require 99, 106, 114, 131, 149 and 164 CEs instead of 100, 107, 115, 132, 150 and 165.
173177

178+
*Changed in version 1.13.1:* sorting 25 inputs requires 130 CEs instead of 131.
179+
180+
*Changed in version 1.13.1:* `index_pair()` is now `[[nodiscard]]` when possible for all `sorting_network_sorter` specializations.
181+
174182

175183
[double-insertion-sort]: Original-research.md#double-insertion-sort
176184
[fixed-sorter-traits]: Sorter-traits.md#fixed_sorter_traits

0 commit comments

Comments
 (0)