Skip to content

Commit 6b691c1

Browse files
committed
init
0 parents  commit 6b691c1

File tree

16 files changed

+2932
-0
lines changed

16 files changed

+2932
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+
cmake-build*

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "extern/metal"]
2+
path = extern/metal
3+
url = https://github.com/brunocodutra/metal.git

CMakeLists.txt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
2+
3+
project(SortingNetworkCpp)
4+
5+
option(BUILD_BENCHMARK "Build the benchmark" ON)
6+
option(BUILD_TESTS "Build the tests" OFF)
7+
8+
include(cmake/Conan.cmake)
9+
run_conan()
10+
11+
set(SN_BENCHMARK_EXECUTABLE_NAME "sorting_network_cpp_benchmark")
12+
set(SN_TESTS_EXECUTABLE_NAME "sorting_network_cpp_tests")
13+
14+
set(CMAKE_CXX_STANDARD 17)
15+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
16+
17+
add_library(project_options INTERFACE)
18+
target_compile_features(project_options INTERFACE cxx_std_17)
19+
20+
if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
21+
set(CMAKE_CXX_FLAGS_RELEASE "/W3 /O2 /fp:fast")
22+
add_compile_options("-bigobj")
23+
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL GNU)
24+
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -ffast-math")
25+
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL Clang)
26+
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -ffast-math")
27+
endif()
28+
29+
include_directories("include")
30+
31+
add_library(metal INTERFACE)
32+
target_include_directories(metal INTERFACE "extern/metal/include")
33+
34+
if (BUILD_BENCHMARK)
35+
add_executable(${SN_BENCHMARK_EXECUTABLE_NAME} "src/benchmark.cpp")
36+
target_link_libraries(${SN_BENCHMARK_EXECUTABLE_NAME} PRIVATE project_options)
37+
endif()
38+
39+
if(BUILD_TESTS)
40+
find_package(GTest REQUIRED)
41+
include(GoogleTest)
42+
enable_testing()
43+
44+
add_executable(${SN_TESTS_EXECUTABLE_NAME} "test/test_case_gen.h" "test/sorting_network_tests.cpp")
45+
target_link_libraries(${SN_TESTS_EXECUTABLE_NAME} GTest::gtest GTest::gtest_main metal project_options)
46+
47+
gtest_discover_tests(${SN_TESTS_EXECUTABLE_NAME})
48+
endif()

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# sorting_network_cpp
2+
3+
sorting_network_cpp offers templated implementations of various types of [sorting networks](https://en.wikipedia.org/wiki/Sorting_network) which can drastically improve performance for small problem sizes of simple data compared to regular sorting algorithms (e.g. `std::sort`).
4+
5+
Following listing shows the execution time for different algorithms to sort one million arrays of each 32 random `float` values on an AMD Ryzen 7 2700x @3.70 GHz (compiled with clang 12.0.0 `-O3`):
6+
7+
| algorithm | time (ms) | speedup vs `std::sort` |
8+
|-----------------------------|:---------:|:----------------------:|
9+
| Batcher Odd Even Merge Sort | 74.10 | 8.4 |
10+
| Bitonic Merge Sort | 86.53 | 7.2 |
11+
| Bose Nelson Sort | 73.05 | 8.6 |
12+
| Bubble Sort | 185.58 | 3.3 |
13+
| Insertion Sort | 189.93 | 3.3 |
14+
| std::sort | 628.74 | 1 |
15+
16+
An interactive overview of performance measurements for the sorting networks on different data types and problem sizes can be found [here](https://raw.githack.com/quxflux/sorting_network_cpp/master/doc/data_explorer.htm).
17+
18+
## Usage
19+
20+
A single class `sorting_network` is available which encapsulates the implementation of the different sorting networks.
21+
22+
Following listing gives a few examples:
23+
24+
```cpp
25+
#include <sorting_network_cpp/sorting_network.hpp>
26+
27+
#include <array>
28+
#include <cstdint>
29+
30+
void example()
31+
{
32+
using namespace quxflux::sorting_net;
33+
34+
static constexpr std::size_t N = 16;
35+
36+
{
37+
std::array<int, N> my_array;
38+
// fill array...
39+
40+
sorting_network<N>{}(my_array.begin());
41+
// my_array is now sorted
42+
}
43+
44+
{
45+
int data[N];
46+
// fill array...
47+
48+
// raw pointers work as well...
49+
sorting_network<N>{}(data);
50+
// data is now sorted
51+
}
52+
53+
{
54+
int data[N];
55+
56+
// by default std::less<T> is used as comparator, custom comparators
57+
// may be passed as follows:
58+
59+
// custom comparators
60+
sorting_network<N>{}(data, compare_and_swap<int, std::less<int>>{});
61+
62+
// function objects
63+
struct predicate
64+
{
65+
constexpr bool operator()(const int a, const int b) const { return a < b; }
66+
};
67+
68+
sorting_network<N>{}(data, compare_and_swap<int, predicate>{});
69+
}
70+
}
71+
```
72+
73+
When no `type` is explicitly specified as template argument for `sorting_network`, the `type::bose_nelson_sort` is used.
74+
75+
Available network types ATM are:
76+
* `insertion_sort`
77+
* `bubble_sort`
78+
* `bose_nelson_sort`
79+
* `batcher_odd_even_merge_sort`
80+
* `bitonic_merge_sort`
81+
* `size_optimized_sort`
82+
83+
Following example shows how to specify a different `type` than the default one:
84+
85+
```cpp
86+
quxflux::sorting_net::sorting_network<N, quxflux::sorting_net::type::bitonic_merge_sort>()(std::begin(data_to_be_sorted));
87+
```
88+
## Using custom compare and swap implementations
89+
90+
The compare and swap operation is the fundamental element a sorting network is composed of. The default implementation works well on scalar types, but if you want to specify a custom implementation (e.g. when hardware intrinsics should be used) you may do this by providing a compare and swap functor to the `sorting_network::operator()` as in following example:
91+
92+
```cpp
93+
#include <sorting_network_cpp/sorting_network.hpp>
94+
95+
#include <array>
96+
97+
void example(std::array<float,3>& arr)
98+
{
99+
quxflux::sorting_net::sorting_network<3>{}(arr, [](float& a, float& b){
100+
const auto b_cpy = b;
101+
b = std::max(a, b);
102+
a = std::min(a, b_cpy);
103+
});
104+
}
105+
```
106+
107+
## Requirements
108+
A compiler with C++17 support
109+
110+
## Dependencies
111+
For the bare sorting functionality no dependencies are required. If you want to run the tests, the needed dependencies will be installed via conan.
112+
113+
## Limitations
114+
* Depending on the implementation of the comparator the performance advantage of a sorting net compared to a regular sorting algorithm (e.g. `std::sort`) may diminish or even result in worse performance. This can be seen in the [interactive benchmark results overview](https://raw.githack.com/quxflux/sorting_network_cpp/master/doc/data_explorer.htm) for the data type `Vec2i Z-order` which causes in most cases all variants of sorting networks being outperformed by `std::sort` (see [src/benchmark.cpp](src/benchmark.cpp) for the implementation of the aforementioned data type).
115+
* msvc will fail compiling larger `insertion_sort` networks in certain (unknown) configurations with `fatal error C1202: recursive type or function dependency context too complex`
116+
117+
## References / Acknowledgements
118+
* ["A Sorting Problem"](https://dl.acm.org/doi/pdf/10.1145/321119.321126) by Bose et al.
119+
* ["Sorting networks and their applications"](https://core.ac.uk/download/pdf/192393620.pdf) by Batcher
120+
* [Vectorized/Static-Sort](https://github.com/Vectorized/Static-Sort) adaption for Bose-Nelson sort
121+
* [HS Flensburg](https://www.inf.hs-flensburg.de/lang/algorithmen/sortieren/networks/oemen.htm) explanation for Batcher's odd-even mergesort
122+
* [HS Flensburg](https://www.inf.hs-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm) explanation for bitonic sort
123+
* [SortHunter](https://github.com/bertdobbelaere/SorterHunter) for size optimized sorting networks
124+
* [Google Test](https://github.com/google/googletest) for testing
125+
* [Chart.js](https://www.chartjs.org/) and [Papa Parse](https://www.papaparse.com/) for the visualization of benchmark results
126+
127+
## License
128+
[GPLv3](LICENSE)

cmake/Conan.cmake

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
macro(run_conan)
2+
# Download automatically, you can also just copy the conan.cmake file
3+
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
4+
message(
5+
STATUS
6+
"Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
7+
file(
8+
DOWNLOAD
9+
"https://raw.githubusercontent.com/conan-io/cmake-conan/release/0.17/conan.cmake"
10+
"${CMAKE_BINARY_DIR}/conan.cmake"
11+
EXPECTED_HASH
12+
SHA256=3bef79da16c2e031dc429e1dac87a08b9226418b300ce004cc125a82687baeef
13+
TLS_VERIFY ON)
14+
endif()
15+
16+
set(ENV{CONAN_REVISIONS_ENABLED} 1)
17+
list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
18+
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR})
19+
20+
include(${CMAKE_BINARY_DIR}/conan.cmake)
21+
22+
# Add (or remove) remotes as needed
23+
# conan_add_remote(NAME conan-center URL https://conan.bintray.com)
24+
conan_add_remote(NAME cci URL https://center.conan.io INDEX 0)
25+
conan_add_remote(
26+
NAME bincrafters URL
27+
https://bincrafters.jfrog.io/artifactory/api/conan/public-conan)
28+
29+
# For multi configuration generators, like VS and XCode
30+
if(NOT CMAKE_CONFIGURATION_TYPES)
31+
message(STATUS "Single configuration build!")
32+
set(LIST_OF_BUILD_TYPES ${CMAKE_BUILD_TYPE})
33+
else()
34+
message(STATUS "Multi-configuration build: '${CMAKE_CONFIGURATION_TYPES}'!")
35+
set(LIST_OF_BUILD_TYPES ${CMAKE_CONFIGURATION_TYPES})
36+
endif()
37+
38+
foreach(TYPE ${LIST_OF_BUILD_TYPES})
39+
message(STATUS "Running Conan for build type '${TYPE}'")
40+
41+
# Detects current build settings to pass into conan
42+
conan_cmake_autodetect(settings BUILD_TYPE ${TYPE})
43+
44+
# PATH_OR_REFERENCE ${CMAKE_SOURCE_DIR} is used to tell conan to process
45+
# the external "conanfile.py" provided with the project
46+
# Alternatively a conanfile.txt could be used
47+
conan_cmake_install(
48+
PATH_OR_REFERENCE
49+
${CMAKE_SOURCE_DIR}
50+
BUILD
51+
missing
52+
# Pass compile-time configured options into conan
53+
OPTIONS
54+
SETTINGS
55+
${settings})
56+
endforeach()
57+
58+
endmacro()

conanfile.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[requires]
2+
gtest/[>=1.11.0]
3+
4+
[options]
5+
6+
[generators]
7+
cmake
8+
cmake_find_package

0 commit comments

Comments
 (0)