├── test ├── linked_mixed │ ├── test.cpp │ ├── f.cpp │ ├── g.mm │ └── CMakeLists.txt ├── single_header_mixed │ ├── test.cpp │ ├── f.cpp │ ├── g.mm │ └── CMakeLists.txt ├── single_header_cpp │ ├── f.cpp │ ├── f.h │ ├── test.cpp │ └── CMakeLists.txt ├── single_header_objcpp_arc │ ├── f.mm │ ├── f.h │ ├── test.mm │ └── CMakeLists.txt ├── single_header_objcpp_noarc │ ├── f.mm │ ├── f.h │ ├── test.mm │ └── CMakeLists.txt ├── linked_objcpp_arc │ ├── test.mm │ └── CMakeLists.txt ├── linked_objcpp_noarc │ ├── test.mm │ └── CMakeLists.txt ├── defines_feature_test_macros │ ├── test.cpp │ └── CMakeLists.txt ├── works_with_objc_types │ ├── test.mm │ └── CMakeLists.txt └── CMakeLists.txt ├── pstld └── pstld.cpp ├── test-msvc-stl ├── bootstrap.sh ├── transform_exclusive_scan.patch ├── parallel_algorithms_reduce.patch ├── parallel_algorithms_transform_reduce.patch ├── stable_sort.patch └── CMakeLists.txt ├── test-llvm-pstl ├── bootstrap.sh ├── helpers.h ├── utils.patch ├── is_sorted.patch ├── uninitialized_fill_destroy.pass.cpp.patch ├── transform_binary.pass.cpp.patch └── CMakeLists.txt ├── benchmark ├── CMakeLists.txt └── benchmark.cpp ├── CMakeLists.txt ├── LICENSE ├── .clang-format ├── .github └── workflows │ └── build.yml └── README.md /test/linked_mixed/test.cpp: -------------------------------------------------------------------------------- 1 | bool f(); 2 | bool g(); 3 | int main() 4 | { 5 | f(); 6 | g(); 7 | } 8 | -------------------------------------------------------------------------------- /test/single_header_mixed/test.cpp: -------------------------------------------------------------------------------- 1 | bool f(); 2 | bool g(); 3 | int main() 4 | { 5 | f(); 6 | g(); 7 | } 8 | -------------------------------------------------------------------------------- /test/single_header_cpp/f.cpp: -------------------------------------------------------------------------------- 1 | #include "f.h" 2 | // this file is here to check there's no linker errors caused by duplicate symbols 3 | -------------------------------------------------------------------------------- /test/single_header_cpp/f.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define PSTLD_HEADER_ONLY 3 | #define PSTLD_HACK_INTO_STD 4 | #include 5 | -------------------------------------------------------------------------------- /test/single_header_objcpp_arc/f.mm: -------------------------------------------------------------------------------- 1 | #include "f.h" 2 | // this file is here to check there's no linker errors caused by duplicate symbols 3 | -------------------------------------------------------------------------------- /test/single_header_objcpp_noarc/f.mm: -------------------------------------------------------------------------------- 1 | #include "f.h" 2 | // this file is here to check there's no linker errors caused by duplicate symbols 3 | -------------------------------------------------------------------------------- /pstld/pstld.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Michael G. Kazakov. All rights reserved. Distributed under the MIT License. 2 | #define PSTLD_INTERNAL_IMPLEMENTATION_FILE 3 | #include "pstld.h" 4 | -------------------------------------------------------------------------------- /test/single_header_objcpp_arc/f.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define PSTLD_HEADER_ONLY 3 | #define PSTLD_HACK_INTO_STD 4 | #include 5 | #include // to check that this file is compiled as Objective-C++ 6 | -------------------------------------------------------------------------------- /test/single_header_objcpp_noarc/f.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define PSTLD_HEADER_ONLY 3 | #define PSTLD_HACK_INTO_STD 4 | #include 5 | #include // to check that this file is compiled as Objective-C++ 6 | -------------------------------------------------------------------------------- /test/single_header_cpp/test.cpp: -------------------------------------------------------------------------------- 1 | #include "f.h" 2 | int main() 3 | { 4 | int v[] = {5, 3, 7, 1, 5}; 5 | std::sort(std::execution::par, std::begin(v), std::end(v)); 6 | return !std::is_sorted(std::begin(v), std::end(v)); 7 | } 8 | -------------------------------------------------------------------------------- /test/single_header_objcpp_arc/test.mm: -------------------------------------------------------------------------------- 1 | #include "f.h" 2 | int main() 3 | { 4 | int v[] = {5, 3, 7, 1, 5}; 5 | std::sort(std::execution::par, std::begin(v), std::end(v)); 6 | return !std::is_sorted(std::begin(v), std::end(v)); 7 | } 8 | -------------------------------------------------------------------------------- /test/single_header_objcpp_noarc/test.mm: -------------------------------------------------------------------------------- 1 | #include "f.h" 2 | int main() 3 | { 4 | int v[] = {5, 3, 7, 1, 5}; 5 | std::sort(std::execution::par, std::begin(v), std::end(v)); 6 | return !std::is_sorted(std::begin(v), std::end(v)); 7 | } 8 | -------------------------------------------------------------------------------- /test/linked_mixed/f.cpp: -------------------------------------------------------------------------------- 1 | #define PSTLD_HACK_INTO_STD 2 | #include 3 | bool f() 4 | { 5 | int v[] = {5, 3, 7, 1, 5}; 6 | std::sort(std::execution::par, std::begin(v), std::end(v)); 7 | return std::is_sorted(std::begin(v), std::end(v)); 8 | } 9 | -------------------------------------------------------------------------------- /test-msvc-stl/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | git clone \ 4 | --depth 1 \ 5 | --filter=blob:none \ 6 | --no-checkout \ 7 | https://github.com/microsoft/STL.git 8 | 9 | cd STL 10 | git sparse-checkout init --cone 11 | git sparse-checkout add tests 12 | git checkout 13 | git apply ../*.patch 14 | -------------------------------------------------------------------------------- /test/single_header_mixed/f.cpp: -------------------------------------------------------------------------------- 1 | #define PSTLD_HEADER_ONLY 2 | #define PSTLD_HACK_INTO_STD 3 | #include 4 | bool f() 5 | { 6 | int v[] = {5, 3, 7, 1, 5}; 7 | std::sort(std::execution::par, std::begin(v), std::end(v)); 8 | return std::is_sorted(std::begin(v), std::end(v)); 9 | } 10 | -------------------------------------------------------------------------------- /test-llvm-pstl/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | git clone \ 4 | --depth 1 \ 5 | --filter=blob:none \ 6 | --no-checkout \ 7 | https://github.com/llvm/llvm-project 8 | 9 | cd llvm-project 10 | git sparse-checkout init --cone 11 | git sparse-checkout add pstl 12 | git checkout 13 | git apply --verbose ../*.patch 14 | -------------------------------------------------------------------------------- /test/linked_mixed/g.mm: -------------------------------------------------------------------------------- 1 | #define PSTLD_HACK_INTO_STD 2 | #include 3 | #include // to check that this file is compiled as Objective-C++ 4 | bool g() 5 | { 6 | int v[] = {5, 3, 7, 1, 5}; 7 | std::sort(std::execution::par, std::begin(v), std::end(v)); 8 | return std::is_sorted(std::begin(v), std::end(v)); 9 | } 10 | -------------------------------------------------------------------------------- /test/linked_objcpp_arc/test.mm: -------------------------------------------------------------------------------- 1 | #define PSTLD_HACK_INTO_STD 2 | #include 3 | #include // to check that this file is compiled as Objective-C++ 4 | int main() 5 | { 6 | int v[] = {5, 3, 7, 1, 5}; 7 | std::sort(std::execution::par, std::begin(v), std::end(v)); 8 | return !std::is_sorted(std::begin(v), std::end(v)); 9 | } 10 | -------------------------------------------------------------------------------- /test/linked_objcpp_noarc/test.mm: -------------------------------------------------------------------------------- 1 | #define PSTLD_HACK_INTO_STD 2 | #include 3 | #include // to check that this file is compiled as Objective-C++ 4 | int main() 5 | { 6 | int v[] = {5, 3, 7, 1, 5}; 7 | std::sort(std::execution::par, std::begin(v), std::end(v)); 8 | return !std::is_sorted(std::begin(v), std::end(v)); 9 | } 10 | -------------------------------------------------------------------------------- /test/single_header_mixed/g.mm: -------------------------------------------------------------------------------- 1 | #define PSTLD_HEADER_ONLY 2 | #define PSTLD_HACK_INTO_STD 3 | #include 4 | #include // to check that this file is compiled as Objective-C++ 5 | bool g() 6 | { 7 | int v[] = {5, 3, 7, 1, 5}; 8 | std::sort(std::execution::par, std::begin(v), std::end(v)); 9 | return std::is_sorted(std::begin(v), std::end(v)); 10 | } 11 | -------------------------------------------------------------------------------- /test/defines_feature_test_macros/test.cpp: -------------------------------------------------------------------------------- 1 | #define PSTLD_HACK_INTO_STD 2 | #include 3 | 4 | // https://en.cppreference.com/w/cpp/utility/feature_test 5 | 6 | #ifndef __cpp_lib_execution 7 | #error __cpp_lib_execution should be defined 8 | #endif 9 | 10 | #ifndef __cpp_lib_parallel_algorithm 11 | #error __cpp_lib_parallel_algorithm should be defined 12 | #endif 13 | 14 | int main(){} 15 | -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | 3 | add_executable(benchmark 4 | benchmark.cpp 5 | ) 6 | 7 | target_link_libraries(benchmark PRIVATE pstld) 8 | set_property(TARGET benchmark PROPERTY CXX_STANDARD 17) 9 | set_target_properties(benchmark PROPERTIES COMPILE_FLAGS "-includepstld/pstld.h -Wall -Wextra -Wpedantic -Werror") 10 | target_compile_definitions(benchmark PRIVATE PSTLD_HACK_INTO_STD) 11 | -------------------------------------------------------------------------------- /test/works_with_objc_types/test.mm: -------------------------------------------------------------------------------- 1 | #define PSTLD_HACK_INTO_STD 2 | #include 3 | #include 4 | int main() 5 | { 6 | NSString *v[] = {@"c", @"d", @"a", @"b", @"e"}; 7 | auto cmp = [](NSString *first, NSString *second) { return [first compare:second] < 0; }; 8 | std::sort(std::execution::par, std::begin(v), std::end(v), cmp); 9 | return !std::is_sorted(std::begin(v), std::end(v), cmp); 10 | } 11 | -------------------------------------------------------------------------------- /test/defines_feature_test_macros/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(_target "custom-defines-feature-tests_macros") 2 | 3 | add_executable(${_target} EXCLUDE_FROM_ALL test.cpp) 4 | 5 | target_link_libraries(${_target} PRIVATE pstld) 6 | 7 | set_target_properties(${_target} PROPERTIES 8 | FOLDER "Tests/Custom" 9 | CXX_STANDARD 17 10 | COMPILE_FLAGS "-Wall -Wextra -Wpedantic -Werror") 11 | 12 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 13 | 14 | add_dependencies(pstld-build-custom-tests ${_target}) 15 | -------------------------------------------------------------------------------- /test-llvm-pstl/helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // a hack to suport running tests from LLVM's PSTL 4 | namespace __pstl { 5 | namespace execution = ::std::execution; 6 | namespace __internal { 7 | template 8 | struct __equal_value { 9 | const T &val; 10 | explicit __equal_value(const T &val) : val(val) {} 11 | template 12 | bool operator()(U &&other) const 13 | { 14 | return std::forward(other) == val; 15 | } 16 | }; 17 | } // namespace __internal 18 | 19 | } // namespace __pstl 20 | -------------------------------------------------------------------------------- /test/linked_objcpp_noarc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(_target "custom-linked-objcpp-noarc") 2 | 3 | add_executable(${_target} EXCLUDE_FROM_ALL test.mm) 4 | 5 | target_link_libraries(${_target} PRIVATE pstld) 6 | 7 | set_target_properties(${_target} PROPERTIES 8 | FOLDER "Tests/Custom" 9 | CXX_STANDARD 17 10 | COMPILE_FLAGS "-Wall -Wextra -Wpedantic -Werror" 11 | LINK_FLAGS "-fobjc-link-runtime") 12 | 13 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 14 | 15 | add_dependencies(pstld-build-custom-tests ${_target}) 16 | -------------------------------------------------------------------------------- /test-llvm-pstl/utils.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pstl/test/support/utils.h b/pstl/test/support/utils.h 2 | index d223204..b24a4a5 100644 3 | --- a/pstl/test/support/utils.h 4 | +++ b/pstl/test/support/utils.h 5 | @@ -787,7 +787,7 @@ struct MakeIterator 6 | 7 | // Useful constant variables 8 | constexpr std::size_t GuardSize = 5; 9 | -constexpr std::ptrdiff_t sizeLimit = 1000; 10 | +constexpr std::ptrdiff_t sizeLimit = 100000; 11 | 12 | template // local iterator_traits for non-iterators 13 | struct iterator_traits_ 14 | -------------------------------------------------------------------------------- /test/linked_mixed/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(_target "custom-linked-mixed") 2 | 3 | add_executable(${_target} EXCLUDE_FROM_ALL test.cpp f.cpp g.mm) 4 | 5 | target_link_libraries(${_target} PRIVATE pstld) 6 | 7 | set_target_properties(${_target} PROPERTIES 8 | FOLDER "Tests/Custom" 9 | CXX_STANDARD 17 10 | COMPILE_FLAGS "-fobjc-arc -Wall -Wextra -Wpedantic -Werror" 11 | LINK_FLAGS "-fobjc-link-runtime") 12 | 13 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 14 | 15 | add_dependencies(pstld-build-custom-tests ${_target}) 16 | -------------------------------------------------------------------------------- /test/linked_objcpp_arc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(_target "custom-linked-objcpp-arc") 2 | 3 | add_executable(${_target} EXCLUDE_FROM_ALL test.mm) 4 | 5 | target_link_libraries(${_target} PRIVATE pstld) 6 | 7 | set_target_properties(${_target} PROPERTIES 8 | FOLDER "Tests/Custom" 9 | CXX_STANDARD 17 10 | COMPILE_FLAGS "-fobjc-arc -Wall -Wextra -Wpedantic -Werror" 11 | LINK_FLAGS "-fobjc-link-runtime") 12 | 13 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 14 | 15 | add_dependencies(pstld-build-custom-tests ${_target}) 16 | -------------------------------------------------------------------------------- /test/single_header_cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(_target "custom-single-header-cpp") 2 | 3 | add_executable(${_target} EXCLUDE_FROM_ALL test.cpp f.h f.cpp) 4 | 5 | target_include_directories( ${_target} PRIVATE $) 6 | 7 | set_target_properties(${_target} PROPERTIES 8 | FOLDER "Tests/Custom" 9 | CXX_STANDARD 17 10 | COMPILE_FLAGS "-Wall -Wextra -Wpedantic -Werror") 11 | 12 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 13 | 14 | add_dependencies(pstld-build-custom-tests ${_target}) 15 | -------------------------------------------------------------------------------- /test/works_with_objc_types/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(_target "custom-works-with-objc-types") 2 | 3 | add_executable(${_target} EXCLUDE_FROM_ALL test.mm) 4 | 5 | target_link_libraries(${_target} PRIVATE pstld) 6 | 7 | set_target_properties(${_target} PROPERTIES 8 | FOLDER "Tests/Custom" 9 | CXX_STANDARD 17 10 | COMPILE_FLAGS "-fobjc-arc -Wall -Wextra -Wpedantic -Werror" 11 | LINK_FLAGS "-fobjc-link-runtime") 12 | 13 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 14 | 15 | add_dependencies(pstld-build-custom-tests ${_target}) 16 | -------------------------------------------------------------------------------- /test/single_header_mixed/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(_target "custom-single-header-mixed") 2 | 3 | add_executable(${_target} EXCLUDE_FROM_ALL test.cpp f.cpp g.mm) 4 | 5 | target_include_directories( ${_target} PRIVATE $) 6 | 7 | set_target_properties(${_target} PROPERTIES 8 | FOLDER "Tests/Custom" 9 | CXX_STANDARD 17 10 | COMPILE_FLAGS "-fobjc-arc -Wall -Wextra -Wpedantic -Werror" 11 | LINK_FLAGS "-fobjc-link-runtime") 12 | 13 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 14 | 15 | add_dependencies(pstld-build-custom-tests ${_target}) 16 | -------------------------------------------------------------------------------- /test/single_header_objcpp_noarc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(_target "custom-single-header-objcpp-noarc") 2 | 3 | add_executable(${_target} EXCLUDE_FROM_ALL test.mm f.h f.mm) 4 | 5 | target_include_directories( ${_target} PRIVATE $) 6 | 7 | set_target_properties(${_target} PROPERTIES 8 | FOLDER "Tests/Custom" 9 | CXX_STANDARD 17 10 | COMPILE_FLAGS "-Wall -Wextra -Wpedantic -Werror" 11 | LINK_FLAGS "-fobjc-link-runtime") 12 | 13 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 14 | 15 | add_dependencies(pstld-build-custom-tests ${_target}) 16 | -------------------------------------------------------------------------------- /test/single_header_objcpp_arc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(_target "custom-single-header-objcpp-arc") 2 | 3 | add_executable(${_target} EXCLUDE_FROM_ALL test.mm f.h f.mm) 4 | 5 | target_include_directories( ${_target} PRIVATE $) 6 | 7 | set_target_properties(${_target} PROPERTIES 8 | FOLDER "Tests/Custom" 9 | CXX_STANDARD 17 10 | COMPILE_FLAGS "-fobjc-arc -Wall -Wextra -Wpedantic -Werror" 11 | LINK_FLAGS "-fobjc-link-runtime") 12 | 13 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 14 | 15 | add_dependencies(pstld-build-custom-tests ${_target}) 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | 3 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 4 | 5 | project(pstld VERSION 1.0 6 | DESCRIPTION "ParallelSTL on top of GCD" 7 | LANGUAGES CXX) 8 | 9 | add_library(pstld 10 | pstld/pstld.h 11 | pstld/pstld.cpp 12 | ) 13 | 14 | target_include_directories(pstld PUBLIC .) 15 | set_property(TARGET pstld PROPERTY CXX_STANDARD 17) 16 | 17 | if (BUILD_TESTING) 18 | enable_testing() 19 | add_subdirectory(benchmark) 20 | add_subdirectory(test) 21 | add_subdirectory(test-llvm-pstl) 22 | add_subdirectory(test-msvc-stl) 23 | endif (BUILD_TESTING) 24 | -------------------------------------------------------------------------------- /test-llvm-pstl/is_sorted.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pstl/test/std/algorithms/alg.sorting/is_sorted.pass.cpp b/pstl/test/std/algorithms/alg.sorting/is_sorted.pass.cpp 2 | index d4c6fe2..9f3ae50 100644 3 | --- a/pstl/test/std/algorithms/alg.sorting/is_sorted.pass.cpp 4 | +++ b/pstl/test/std/algorithms/alg.sorting/is_sorted.pass.cpp 5 | @@ -28,6 +28,7 @@ struct test_is_sorted 6 | typedef typename std::iterator_traits::value_type T; 7 | 8 | //try random-access iterator 9 | + exam = is_sorted(first, last); 10 | bool res = is_sorted(exec, first, last); 11 | EXPECT_TRUE(exam == res, "is_sorted wrong result for random-access iterator"); 12 | auto iexam = is_sorted_until(first, last); 13 | -------------------------------------------------------------------------------- /test-msvc-stl/transform_exclusive_scan.patch: -------------------------------------------------------------------------------- 1 | diff --git a/tests/std/tests/P0024R2_parallel_algorithms_transform_exclusive_scan/test.cpp b/tests/std/tests/P0024R2_parallel_algorithms_transform_exclusive_scan/test.cpp 2 | index 256f613..0dda432 100644 3 | --- a/tests/std/tests/P0024R2_parallel_algorithms_transform_exclusive_scan/test.cpp 4 | +++ b/tests/std/tests/P0024R2_parallel_algorithms_transform_exclusive_scan/test.cpp 5 | @@ -189,7 +189,6 @@ struct typesBop { 6 | void test_case_transform_exclusive_scan_init_writes_intermediate_type() { 7 | inputType input[2]{0, 0}; 8 | outputType output[2]{0, 0}; 9 | - transform_exclusive_scan(begin(input), end(input), output, intermediateType{0}, typesBop{}, transformUop{}); 10 | transform_exclusive_scan(par, begin(input), end(input), output, intermediateType{0}, typesBop{}, transformUop{}); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_custom_target(pstld-build-custom-tests 2 | COMMENT "Build all the custom tests.") 3 | set_target_properties(pstld-build-custom-tests PROPERTIES FOLDER "Tests/Custom") 4 | 5 | add_custom_target(check-pstld-custom 6 | COMMAND "${CMAKE_CTEST_COMMAND}" --output-on-failure 7 | USES_TERMINAL 8 | DEPENDS pstld-build-custom-tests 9 | COMMENT "Build and run all the unit tests.") 10 | set_target_properties(check-pstld-custom PROPERTIES FOLDER "Tests/Custom") 11 | 12 | add_subdirectory(defines_feature_test_macros) 13 | add_subdirectory(linked_objcpp_arc) 14 | add_subdirectory(linked_objcpp_noarc) 15 | add_subdirectory(linked_mixed) 16 | add_subdirectory(single_header_cpp) 17 | add_subdirectory(single_header_mixed) 18 | add_subdirectory(single_header_objcpp_arc) 19 | add_subdirectory(single_header_objcpp_noarc) 20 | add_subdirectory(works_with_objc_types) 21 | -------------------------------------------------------------------------------- /test-llvm-pstl/uninitialized_fill_destroy.pass.cpp.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pstl/test/std/utilities/memory/specialized.algorithms/uninitialized_fill_destroy.pass.cpp b/pstl/test/std/utilities/memory/specialized.algorithms/uninitialized_fill_destroy.pass.cpp 2 | index d579ea5..1e26be2 100644 3 | --- a/pstl/test/std/utilities/memory/specialized.algorithms/uninitialized_fill_destroy.pass.cpp 4 | +++ b/pstl/test/std/utilities/memory/specialized.algorithms/uninitialized_fill_destroy.pass.cpp 5 | @@ -71,9 +71,11 @@ test_uninitialized_fill_destroy_by_type() 6 | std::size_t N = 100000; 7 | for (size_t n = 0; n <= N; n = n <= 16 ? n + 1 : size_t(3.1415 * n)) 8 | { 9 | - std::unique_ptr p(new T[n]); 10 | - invoke_on_all_policies(test_uninitialized_fill_destroy(), p.get(), std::next(p.get(), n), T(), n, 11 | + unsigned char *mem = new unsigned char[sizeof(T)*n]; 12 | + invoke_on_all_policies(test_uninitialized_fill_destroy(),(T*)mem, std::next((T*)mem, n), T(), n, 13 | std::is_trivial()); 14 | + delete [] mem; 15 | + 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Michael G. Kazakov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /test-msvc-stl/parallel_algorithms_reduce.patch: -------------------------------------------------------------------------------- 1 | diff --git a/tests/std/tests/P0024R2_parallel_algorithms_reduce/test.cpp b/tests/std/tests/P0024R2_parallel_algorithms_reduce/test.cpp 2 | index c168a29..1cf70e4 100644 3 | --- a/tests/std/tests/P0024R2_parallel_algorithms_reduce/test.cpp 4 | +++ b/tests/std/tests/P0024R2_parallel_algorithms_reduce/test.cpp 5 | @@ -55,7 +55,7 @@ void test_case_move_only(ExPo&& exec, const size_t testSize) { 6 | // algorithms, but the standard is unclear here and if this isn't allowed, the standard 7 | // is bad and should feel bad 8 | auto testData = get_move_only_test_data(testSize); 9 | - unique_ptr> result = reduce(forward(exec), make_move_iterator(testData.begin()), 10 | + unique_ptr> result = reduce(std::forward(exec), make_move_iterator(testData.begin()), 11 | make_move_iterator(testData.end()), make_unique>(), 12 | [](unique_ptr> lhs, unique_ptr> rhs) { 13 | lhs->insert(lhs->end(), rhs->begin(), rhs->end()); 14 | -------------------------------------------------------------------------------- /test-llvm-pstl/transform_binary.pass.cpp.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pstl/test/std/algorithms/alg.modifying.operations/transform_binary.pass.cpp b/pstl/test/std/algorithms/alg.modifying.operations/transform_binary.pass.cpp 2 | index 949e6f5..f32fb72 100644 3 | --- a/pstl/test/std/algorithms/alg.modifying.operations/transform_binary.pass.cpp 4 | +++ b/pstl/test/std/algorithms/alg.modifying.operations/transform_binary.pass.cpp 5 | @@ -41,7 +41,7 @@ check_and_reset(InputIterator1 first1, InputIterator1 last1, InputIterator2 firs 6 | for (; first1 != last1; ++first1, ++first2, ++out_first, ++k) 7 | { 8 | // check 9 | - Out expected = Out(1.5) + *first1 - *first2; 10 | + Out expected = std::clamp(Out(1.5) + *first1 - *first2, std::numeric_limits::lowest(), std::numeric_limits::max()); 11 | Out actual = *out_first; 12 | if (std::is_floating_point::value) 13 | { 14 | @@ -113,7 +113,7 @@ main() 15 | test(non_const(TheOperation(1.5))); 16 | test(non_const(TheOperation(1.5))); 17 | //lambda 18 | - test([](const int8_t& x, const float64_t& y) { return int8_t(int8_t(1.5) + x - y); }); 19 | + test([](const int8_t& x, const float64_t& y) { return int8_t(std::clamp(int8_t(1.5) + x - y, std::numeric_limits::lowest(), std::numeric_limits::max())); }); 20 | 21 | test_algo_basic_double(run_for_rnd_fw>()); 22 | 23 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | --- 4 | Language: Cpp 5 | Standard: c++17 6 | DerivePointerAlignment: false 7 | PointerAlignment: Right 8 | ColumnLimit: 100 9 | AccessModifierOffset: -4 10 | AllowShortFunctionsOnASingleLine: InlineOnly 11 | AllowAllParametersOfDeclarationOnNextLine: false 12 | AllowShortEnumsOnASingleLine: false 13 | AlwaysBreakTemplateDeclarations: Yes 14 | BinPackArguments: false 15 | BinPackParameters: false 16 | BreakBeforeBraces: Custom 17 | BraceWrapping: 18 | AfterFunction: true 19 | AfterClass: true 20 | AfterStruct: false 21 | AfterEnum: false 22 | BeforeElse: true 23 | BreakStringLiterals: true 24 | FixNamespaceComments: true 25 | IndentCaseLabels: true 26 | IndentPPDirectives: BeforeHash 27 | SortIncludes: false 28 | SpaceBeforeParens: Never 29 | SpacesInConditionalStatement: true 30 | SpacesInContainerLiterals: false 31 | --- 32 | Language: ObjC 33 | Standard: Cpp11 34 | DerivePointerAlignment: false 35 | PointerAlignment: Right 36 | ColumnLimit: 100 37 | AccessModifierOffset: -4 38 | AllowShortFunctionsOnASingleLine: InlineOnly 39 | AllowAllParametersOfDeclarationOnNextLine: false 40 | AllowShortEnumsOnASingleLine: false 41 | AlwaysBreakTemplateDeclarations: Yes 42 | BinPackArguments: false 43 | BinPackParameters: false 44 | BreakBeforeBraces: Custom 45 | BraceWrapping: 46 | AfterFunction: true 47 | AfterClass: true 48 | AfterStruct: false 49 | AfterEnum: false 50 | BeforeElse: true 51 | BreakStringLiterals: true 52 | FixNamespaceComments: true 53 | IndentCaseLabels: true 54 | SortIncludes: false 55 | SpaceBeforeParens: Never 56 | SpacesInConditionalStatement: true 57 | SpacesInContainerLiterals: false 58 | -------------------------------------------------------------------------------- /test-msvc-stl/parallel_algorithms_transform_reduce.patch: -------------------------------------------------------------------------------- 1 | diff --git a/tests/std/tests/P0024R2_parallel_algorithms_transform_reduce/test.cpp b/tests/std/tests/P0024R2_parallel_algorithms_transform_reduce/test.cpp 2 | index 9141838..096dd68 100644 3 | --- a/tests/std/tests/P0024R2_parallel_algorithms_transform_reduce/test.cpp 4 | +++ b/tests/std/tests/P0024R2_parallel_algorithms_transform_reduce/test.cpp 5 | @@ -88,7 +88,7 @@ void test_case_move_only_binary(ExPo&& exec, const size_t testSize) { 6 | auto testData1 = get_move_only_test_data(testSize); 7 | auto testData2 = get_move_only_test_data(testSize); 8 | unique_ptr> result = transform_reduce( 9 | - forward(exec), make_move_iterator(testData1.begin()), make_move_iterator(testData1.end()), 10 | + std::forward(exec), make_move_iterator(testData1.begin()), make_move_iterator(testData1.end()), 11 | make_move_iterator(testData2.begin()), make_unique>(), 12 | [](unique_ptr> lhs, unique_ptr> rhs) { 13 | lhs->insert(lhs->end(), rhs->begin(), rhs->end()); 14 | @@ -114,7 +114,7 @@ template 15 | void test_case_move_only(ExPo&& exec, const size_t testSize) { 16 | auto testData = get_move_only_test_data(testSize); 17 | unique_ptr> result = transform_reduce( 18 | - forward(exec), make_move_iterator(testData.begin()), make_move_iterator(testData.end()), 19 | + std::forward(exec), make_move_iterator(testData.begin()), make_move_iterator(testData.end()), 20 | make_unique>(), 21 | [](unique_ptr> lhs, unique_ptr> rhs) { 22 | lhs->insert(lhs->end(), rhs->begin(), rhs->end()); 23 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | on: [push, workflow_dispatch] 3 | jobs: 4 | build: 5 | strategy: 6 | matrix: 7 | xcode-version: [14.3.1, 15.0.1] 8 | runs-on: [macos-13] 9 | steps: 10 | - name: Select Xcode version 11 | run: sudo xcode-select -s '/Applications/Xcode_${{ matrix.xcode-version }}.app' 12 | - uses: actions/checkout@v2 13 | with: 14 | submodules: 'recursive' 15 | - name: Download LLVM and MSVC unit tests 16 | run: | 17 | cd test-llvm-pstl && ./bootstrap.sh && cd - 18 | cd test-msvc-stl && ./bootstrap.sh 19 | - name: Build and run unit tests (Debug) 20 | run: | 21 | mkdir build-debug 22 | cd build-debug 23 | cmake -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Debug .. 24 | make -j all pstld-build-llvm-tests pstld-build-msvc-tests pstld-build-custom-tests 25 | make test 26 | - name: Build and run unit tests (Release) 27 | run: | 28 | mkdir build-release 29 | cd build-release 30 | cmake -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release .. 31 | make -j all pstld-build-llvm-tests pstld-build-msvc-tests pstld-build-custom-tests 32 | make test 33 | - name: Build and run unit tests (ASAN) 34 | run: | 35 | mkdir build-asan 36 | cd build-asan 37 | cmake -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-fno-omit-frame-pointer -fsanitize=address" .. 38 | make -j all pstld-build-llvm-tests pstld-build-msvc-tests pstld-build-custom-tests 39 | make test 40 | - name: Build and run unit tests (UBSAN) 41 | run: | 42 | mkdir build-ubsan 43 | cd build-ubsan 44 | cmake -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-fno-omit-frame-pointer -fsanitize=undefined" .. 45 | make -j all pstld-build-llvm-tests pstld-build-msvc-tests pstld-build-custom-tests 46 | make test 47 | -------------------------------------------------------------------------------- /test-msvc-stl/stable_sort.patch: -------------------------------------------------------------------------------- 1 | diff --git a/tests/std/tests/P0024R2_parallel_algorithms_stable_sort/test.cpp b/tests/std/tests/P0024R2_parallel_algorithms_stable_sort/test.cpp 2 | index 2ffb607..48e6e8a 100644 3 | --- a/tests/std/tests/P0024R2_parallel_algorithms_stable_sort/test.cpp 4 | +++ b/tests/std/tests/P0024R2_parallel_algorithms_stable_sort/test.cpp 5 | @@ -65,16 +65,16 @@ void assert_stable_sort_cmpTens_test_case(FwdIt first, FwdIt last) { 6 | } 7 | } 8 | 9 | -void test_case_stable_sort_parallel_list(const size_t testSize, mt19937& gen) { 10 | - list c; 11 | - { 12 | - const auto cVec = get_test_case_vector(testSize, gen); 13 | - c.assign(cVec.begin(), cVec.end()); 14 | - } 15 | - 16 | - stable_sort(par, c.begin(), c.end(), cmpTens); 17 | - assert_stable_sort_cmpTens_test_case(c.begin(), c.end()); 18 | -} 19 | +//void test_case_stable_sort_parallel_list(const size_t testSize, mt19937& gen) { 20 | +// list c; 21 | +// { 22 | +// const auto cVec = get_test_case_vector(testSize, gen); 23 | +// c.assign(cVec.begin(), cVec.end()); 24 | +// } 25 | +// 26 | +// stable_sort(par, c.begin(), c.end(), cmpTens); 27 | +// assert_stable_sort_cmpTens_test_case(c.begin(), c.end()); 28 | +//} 29 | 30 | void test_case_stable_sort_parallel_vector(const size_t testSize, mt19937& gen) { 31 | auto c = get_test_case_vector(testSize, gen); 32 | @@ -85,8 +85,8 @@ void test_case_stable_sort_parallel_vector(const size_t testSize, mt19937& gen) 33 | int main() { 34 | mt19937 gen(1729); 35 | 36 | - test_case_stable_sort_parallel_special_cases(); 37 | +// test_case_stable_sort_parallel_special_cases(); 38 | test_case_stable_sort_parallel_special_cases(); 39 | - parallel_test_case(test_case_stable_sort_parallel_list, gen); 40 | +// parallel_test_case(test_case_stable_sort_parallel_list, gen); 41 | parallel_test_case(test_case_stable_sort_parallel_vector, gen); 42 | } 43 | -------------------------------------------------------------------------------- /test-msvc-stl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_custom_target(pstld-build-msvc-tests 2 | COMMENT "Build all the msvc-stl tests.") 3 | set_target_properties(pstld-build-msvc-tests PROPERTIES FOLDER "Tests/MSVC") 4 | 5 | add_custom_target(check-pstld-msvc 6 | COMMAND "${CMAKE_CTEST_COMMAND}" --output-on-failure 7 | USES_TERMINAL 8 | DEPENDS pstld-build-msvc-tests 9 | COMMENT "Build and run all the unit tests.") 10 | set_target_properties(check-pstld-msvc PROPERTIES FOLDER "Tests/MSVC") 11 | 12 | set(UNIT_TESTS 13 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_adjacent_difference/test.cpp 14 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_adjacent_find/test.cpp 15 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_all_of/test.cpp 16 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_count/test.cpp 17 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_equal/test.cpp 18 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_exclusive_scan/test.cpp 19 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_find/test.cpp 20 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_find_end/test.cpp 21 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_find_first_of/test.cpp 22 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_for_each/test.cpp 23 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_inclusive_scan/test.cpp 24 | # ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_is_heap/test.cpp 25 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_is_partitioned/test.cpp 26 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_is_sorted/test.cpp 27 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_mismatch/test.cpp 28 | # ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_partition/test.cpp 29 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_reduce/test.cpp 30 | # ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_remove/test.cpp 31 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_replace/test.cpp 32 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_search/test.cpp 33 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_search_n/test.cpp 34 | # ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_set_difference/test.cpp 35 | # ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_set_intersection/test.cpp 36 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_sort/test.cpp 37 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_stable_sort/test.cpp 38 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_transform/test.cpp 39 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_transform_exclusive_scan/test.cpp 40 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_transform_inclusive_scan/test.cpp 41 | ${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/tests/P0024R2_parallel_algorithms_transform_reduce/test.cpp 42 | ) 43 | 44 | foreach(_file IN LISTS UNIT_TESTS) 45 | file(RELATIVE_PATH _target "${CMAKE_CURRENT_LIST_DIR}" "${_file}") 46 | string(REPLACE "STL/tests/std/tests/" "" _target "${_target}") 47 | string(REPLACE "/test.cpp" "" _target "${_target}") 48 | string(REPLACE "/" "-" _target "${_target}") 49 | set(_target "msvc-${_target}") 50 | 51 | add_executable(${_target} EXCLUDE_FROM_ALL "${_file}") 52 | target_include_directories(${_target} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/STL/tests/std/include") 53 | target_link_libraries(${_target} PRIVATE pstld) 54 | set_target_properties(${_target} PROPERTIES 55 | FOLDER "Tests/MSVC" 56 | COMPILE_FLAGS "-includepstld/pstld.h -Wall -Wextra -Wpedantic -Werror -Wno-unknown-pragmas -UNDEBUG" 57 | CXX_STANDARD 17 58 | CXX_EXTENSIONS NO 59 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 60 | COMPILE_DEFINITIONS PSTLD_HACK_INTO_STD ) 61 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 62 | add_dependencies(pstld-build-msvc-tests ${_target}) 63 | endforeach() 64 | -------------------------------------------------------------------------------- /test-llvm-pstl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_custom_target(pstld-build-llvm-tests 2 | COMMENT "Build all the pstl tests.") 3 | set_target_properties(pstld-build-llvm-tests PROPERTIES FOLDER "Tests/LLVM") 4 | 5 | add_custom_target(check-pstld-llvm 6 | COMMAND "${CMAKE_CTEST_COMMAND}" --output-on-failure 7 | USES_TERMINAL 8 | DEPENDS pstld-build-llvm-tests 9 | COMMENT "Build and run all the unit tests.") 10 | set_target_properties(check-pstld-llvm PROPERTIES FOLDER "Tests/LLVM") 11 | 12 | set(UNIT_TESTS 13 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.modifying.operations/copy_move.pass.cpp 14 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.modifying.operations/fill.pass.cpp 15 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.modifying.operations/generate.pass.cpp 16 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.modifying.operations/replace.pass.cpp 17 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.modifying.operations/replace_copy.pass.cpp 18 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.modifying.operations/swap_ranges.pass.cpp 19 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.modifying.operations/transform_binary.pass.cpp 20 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.modifying.operations/transform_unary.pass.cpp 21 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.modifying.operations/alg.reverse/reverse.pass.cpp 22 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.modifying.operations/alg.partitions/is_partitioned.pass.cpp 23 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/adjacent_find.pass.cpp 24 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/all_of.pass.cpp 25 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/any_of.pass.cpp 26 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/count.pass.cpp 27 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/equal.pass.cpp 28 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/find.pass.cpp 29 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/find_end.pass.cpp 30 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/find_first_of.pass.cpp 31 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/find_if.pass.cpp 32 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/for_each.pass.cpp 33 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/mismatch.pass.cpp 34 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/none_of.pass.cpp 35 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.nonmodifying/search_n.pass.cpp 36 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.sorting/is_sorted.pass.cpp 37 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.sorting/sort.pass.cpp 38 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp 39 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.sorting/alg.min.max/minmax_element.pass.cpp 40 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/algorithms/alg.merge/merge.pass.cpp 41 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/numerics/numeric.ops/adjacent_difference.pass.cpp 42 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/numerics/numeric.ops/reduce.pass.cpp 43 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/numerics/numeric.ops/scan.pass.cpp 44 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/numerics/numeric.ops/transform_reduce.pass.cpp 45 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/numerics/numeric.ops/transform_scan.pass.cpp 46 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/utilities/memory/specialized.algorithms/uninitialized_construct.pass.cpp 47 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/utilities/memory/specialized.algorithms/uninitialized_copy_move.pass.cpp 48 | ${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test/std/utilities/memory/specialized.algorithms/uninitialized_fill_destroy.pass.cpp 49 | ) 50 | 51 | foreach(_file IN LISTS UNIT_TESTS) 52 | file(RELATIVE_PATH _target "${CMAKE_CURRENT_LIST_DIR}" "${_file}") 53 | string(REPLACE "llvm-project/pstl/test/std/" "" _target "${_target}") 54 | string(REPLACE ".pass.cpp" "" _target "${_target}") 55 | string(REPLACE "/" "-" _target "${_target}") 56 | set(_target "llvm-${_target}") 57 | 58 | add_executable(${_target} EXCLUDE_FROM_ALL "${_file}") 59 | target_include_directories(${_target} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/llvm-project/pstl/test") 60 | target_link_libraries(${_target} PRIVATE pstld) 61 | set_target_properties(${_target} PROPERTIES 62 | FOLDER "Tests/LLVM" 63 | COMPILE_FLAGS "-includepstld/pstld.h -includetest-llvm-pstl/helpers.h -Wall -Wextra -Wpedantic -Werror -Wno-shorten-64-to-32 -Wno-deprecated-copy" 64 | CXX_STANDARD 17 65 | CXX_EXTENSIONS NO 66 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 67 | COMPILE_DEFINITIONS PSTLD_HACK_INTO_STD ) 68 | add_test(${_target} "${CMAKE_CURRENT_BINARY_DIR}/${_target}") 69 | add_dependencies(pstld-build-llvm-tests ${_target}) 70 | endforeach() 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Build Status](https://github.com/mikekazakov/pstld/actions/workflows/build.yml/badge.svg) 2 | 3 | # pstld 4 | Experimental implementation of ParallelSTL on top of Grand Central Dispatch aka libdispatch 5 | 6 | The purpose of this library is to provide a drop-in implementation of C++ parallel algorithms for the Apple platforms. 7 | Xcode comes with no parallel algorithms in libc++, so this library aims to fill the gap. 8 | pstld uses the native scheduler (libdispatch) and does not depend on any 3rd-party libraries. 9 | 10 | ## Usage 11 | 12 | The simplest way to use the library is to consume it as header-only and to request exposing the parallel algorithms in the namespace ```std```: 13 | ```C++ 14 | #include 15 | #include 16 | #define PSTLD_HEADER_ONLY // no prebuilt library, only the header 17 | #define PSTLD_HACK_INTO_STD // export into namespace std 18 | #include "pstld.h" 19 | int main() 20 | { 21 | std::vector v(100'000'000); 22 | std::generate(v.begin(), v.end(), [] { return std::rand(); }); 23 | std::cout << std::reduce(std::execution::par, v.begin(), v.end()) << std::endl; 24 | } 25 | ``` 26 | The default Xcode toolchain can now build it: 27 | ```Shell 28 | % clang++ -std=c++17 main.cpp -o test && ./test 29 | 64818392 30 | ``` 31 | The same functions can be used directly from the ```pstld``` namespace without specifying the execution policy: 32 | ```C++ 33 | #include 34 | #include 35 | #define PSTLD_HEADER_ONLY // no prebuilt library, only the header 36 | #include "pstld.h" 37 | int main() 38 | { 39 | std::vector v(100'000'000); 40 | std::generate(v.begin(), v.end(), [] { return std::rand(); }); 41 | std::cout << pstld::reduce(v.begin(), v.end()) << std::endl; 42 | } 43 | ``` 44 | pstld can be precompiled via CMake or manually by adding ```pstld.cpp``` into the build sources: 45 | ```C++ 46 | #include 47 | #include 48 | #include "pstld.h" 49 | int main() 50 | { 51 | std::vector v(100'000'000); 52 | std::generate(v.begin(), v.end(), [] { return std::rand(); }); 53 | std::cout << pstld::reduce(v.begin(), v.end()) << std::endl; 54 | } 55 | ``` 56 | ```Shell 57 | % clang++ -std=c++17 main.cpp pstld.cpp -o test && ./test 58 | 64818392 59 | ``` 60 | 61 | ## Completeness 62 | 63 | The library is not complete, this table shows which algorithms are currently available: 64 | 65 | § | Function | Exposed | Parallel 66 | :--- |:--- |:---:|:---: 67 | 25.6.1 | std::all_of | ✅ | ✅ 68 | 25.6.2 | std::any_of | ✅ | ✅ 69 | 25.6.3 | std::none_of | ✅ | ✅ 70 | 25.6.4 | std::for_each | ✅ | ✅ 71 | | | std::for_each_n | ✅ | ✅ 72 | 25.6.5 | std::find | ✅ | ✅ 73 | | | std::find_if | ✅ | ✅ 74 | | | std::find_if_not | ✅ | ✅ 75 | 25.6.6 | std::find_end | ✅ | ✅ 76 | 25.6.7 | std::find_first_of | ✅ | ✅ 77 | 25.6.8 | std::adjacent_find | ✅ | ✅ 78 | 25.6.9 | std::count | ✅ | ✅ 79 | | | std::count_if | ✅ | ✅ 80 | 25.6.10 | std::mismatch | ✅ | ✅ 81 | 25.6.11 | std::equal | ✅ | ✅ 82 | 25.6.13 | std::search | ✅ | ✅ 83 | | | std::search_n | ✅ | ✅ 84 | 25.7.1 | std::copy | ✅ | ✅ 85 | | | std::copy_n | ✅ | ✅ 86 | | | std::copy_if | ✅ | ❌ 87 | 25.7.2 | std::move | ✅ | ✅ 88 | 25.7.3 | std::swap_ranges | ✅ | ✅ 89 | 25.7.4 | std::transform | ✅ | ✅ 90 | 25.7.5 | std::replace | ✅ | ✅ 91 | | | std::replace_if | ✅ | ✅ 92 | | | std::replace_copy | ✅ | ❌ 93 | | | std::replace_copy_if | ✅ | ❌ 94 | 25.7.6 | std::fill | ✅ | ✅ 95 | | | std::fill_n | ✅ | ✅ 96 | 25.7.7 | std::generate | ✅ | ✅ 97 | | | std::generate_n | ✅ | ✅ 98 | 25.7.8 | std::remove | ❌ | ❌ 99 | | | std::remove_if | ❌ | ❌ 100 | | | std::remove_copy | ❌ | ❌ 101 | | | std::remove_copy_if | ❌ | ❌ 102 | 25.7.9 | std::unique | ❌ | ❌ 103 | | | std::unique_copy | ❌ | ❌ 104 | 25.7.10 | std::reverse | ✅ | ✅ 105 | | | std::reverse_copy | ❌ | ❌ 106 | 25.7.11 | std::rotate | ❌ | ❌ 107 | 25.7.14 | std::shift_left | ❌ | ❌ 108 | | | std::shift_right | ❌ | ❌ 109 | 25.8.2.1 | std::sort | ✅ | ✅ 110 | 25.8.2.2 | std::stable_sort | ✅ | ✅ 111 | 25.8.2.3 | std::partial_sort | ❌ | ❌ 112 | 25.8.2.4 | std::partial_sort_copy | ❌ | ❌ 113 | 25.8.2.5 | std::is_sorted | ✅ | ✅ 114 | | | std::is_sorted_until | ✅ | ✅ 115 | 25.8.3 | std::nth_element | ❌ | ❌ 116 | 25.8.5 | std::is_partitioned | ✅ | ✅ 117 | | | std::partition | ❌ | ❌ 118 | | | std::stable_partition | ❌ | ❌ 119 | | | std::partition_copy | ❌ | ❌ 120 | 25.8.6 | std::merge | ✅ | ✅ 121 | | | std::inplace_merge | ❌ | ❌ 122 | 25.8.7.2 | std::includes | ❌ | ❌ 123 | 25.8.7.3 | std::set_union | ❌ | ❌ 124 | 25.8.7.4 | std::set_intersection | ❌ | ❌ 125 | 25.8.7.5 | std::set_difference | ❌ | ❌ 126 | 25.8.7.6 | std::set_symmetric_difference | ❌ | ❌ 127 | 25.8.8.6 | std::is_heap | ❌ | ❌ 128 | | | std::is_heap_until | ❌ | ❌ 129 | 25.8.9 | std::min_element | ✅ | ✅ 130 | | | std::max_element | ✅ | ✅ 131 | | | std::minmax_element | ✅ | ✅ 132 | 25.8.11 | std::lexicographical_compare | ✅ | ✅ 133 | 25.10.4 | std::reduce | ✅ | ✅ 134 | 25.10.6 | std::transform_reduce | ✅ | ✅ 135 | 25.10.8 | std::exclusive_scan | ✅ | ✅ 136 | 25.10.9 | std::inclusive_scan | ✅ | ✅ 137 | 25.10.10 | std::transform_exclusive_scan | ✅ | ✅ 138 | 25.10.11 | std::transform_inclusive_scan | ✅ | ✅ 139 | 25.10.12 | std::adjacent_difference | ✅ | ✅ 140 | 25.11.3 | std::uninitialized_default_construct | ✅ | ✅ 141 | | | std::uninitialized_default_construct_n | ✅ | ✅ 142 | 25.11.4 | std::uninitialized_value_construct | ✅ | ✅ 143 | | | std::uninitialized_value_construct_n | ✅ | ✅ 144 | 25.11.5 | std::uninitialized_copy | ✅ | ✅ 145 | | | std::uninitialized_copy_n | ✅ | ✅ 146 | 25.11.6 | std::uninitialized_move | ✅ | ✅ 147 | | | std::uninitialized_move_n | ✅ | ✅ 148 | 25.11.7 | std::uninitialized_fill | ✅ | ✅ 149 | | | std::uninitialized_fill_n | ✅ | ✅ 150 | 25.11.9 | std::destroy | ✅ | ✅ 151 | | | std::destroy_n | ✅ | ✅ 152 | 153 | ## Parallel speedup 154 | 155 | ```benchmark.cpp``` contains a set of primitive synthetic performance tests. 156 | Each row shows how the parallel implementation compares to its serial counterpart depending on the number of elements in a working set. 157 | The serial variant executes the default algorithms from libc++ and the parallel one runs the pstld implementation. 158 | Per-element operations are mostly trivial in these benchmarks, so the speed-up numbers represent a somewhat worst-case scenario. 159 | 160 |
161 | 2020 MacBook Pro 13" (Apple M1 CPU, 4P+4E cores), macOS 13, Xcode 14 162 | 163 | ``` 164 | 1K 10K 100K 1M 10M 100M 165 | all_of 0.36 0.41 1.41 2.67 2.16 3.46 166 | any_of 0.52 0.41 0.97 1.83 2.24 3.50 167 | none_of 0.40 0.43 0.86 1.84 2.10 3.11 168 | for_each 0.14 0.75 0.55 0.82 0.99 1.74 169 | find 0.21 0.71 0.80 1.75 2.11 3.16 170 | find_end 0.12 0.14 0.23 0.59 0.67 1.02 171 | find_first_of 0.46 0.97 1.71 4.33 4.90 4.95 172 | adjacent_find 0.46 0.50 0.64 1.59 2.03 2.23 173 | count 0.16 0.64 0.93 0.78 0.93 2.24 174 | mismatch 0.29 0.67 0.67 1.12 1.13 1.17 175 | equal 0.48 0.78 0.75 1.21 1.24 1.29 176 | search 0.54 0.69 1.39 3.25 3.79 3.98 177 | copy 0.28 0.65 0.50 0.82 0.97 1.80 178 | move 0.53 0.43 1.12 1.10 1.72 2.18 179 | swap_ranges 0.32 0.66 0.57 0.64 0.97 1.51 180 | transform 0.29 0.85 0.40 0.72 0.83 1.68 181 | replace 0.16 0.31 0.32 1.02 0.98 1.73 182 | fill 0.09 0.78 0.70 0.76 0.72 1.29 183 | reverse 0.29 0.40 0.58 1.31 1.02 0.98 184 | sort_Rnd 0.14 1.64 2.95 3.72 4.32 4.31 185 | sort_Eq 0.07 0.42 0.59 0.94 0.98 0.94 186 | sort_Asc 0.08 0.10 0.33 0.39 0.43 0.39 187 | sort_Des 0.07 0.49 0.62 0.94 0.98 0.96 188 | stable_sort 0.71 1.77 3.08 3.77 4.00 4.09 189 | is_sorted 0.28 0.31 0.96 1.87 2.10 3.19 190 | is_partitioned 0.24 0.68 0.95 1.84 2.10 3.05 191 | merge 1.00 0.52 0.76 1.24 1.96 2.26 192 | minmax_element 1.17 1.04 2.67 5.68 7.22 6.84 193 | lexicographical_compare 0.65 1.02 1.30 2.07 2.14 2.22 194 | reduce 1.13 2.01 1.56 4.28 5.57 5.25 195 | transform_reduce 0.99 1.33 1.28 4.39 5.56 5.40 196 | exclusive_scan 0.60 0.47 0.91 2.34 2.24 2.47 197 | inclusive_scan 0.49 0.63 0.83 2.24 2.24 2.45 198 | transform_exclusive_scan 0.45 0.48 1.06 2.30 2.29 2.33 199 | transform_inclusive_scan 0.37 0.49 1.70 2.26 2.32 2.36 200 | adjacent_difference 0.19 0.79 0.49 0.70 0.79 0.78 201 | uninitialized_value_construct 0.26 0.24 0.40 0.97 1.86 1.89 202 | uninitialized_copy 0.30 0.59 1.86 1.37 2.05 2.54 203 | uninitialized_move 0.56 0.48 1.13 1.25 1.06 2.22 204 | uninitialized_fill 0.76 0.93 3.21 4.94 5.21 5.11 205 | destroy 0.33 0.81 1.68 1.90 1.93 4.13 206 | ``` 207 |
208 | 209 |
210 | 2018 Mac Mini (Intel 8500B CPU, 6 cores), macOS 13, Xcode 14 211 | 212 | ``` 213 | 1K 10K 100K 1M 10M 100M 214 | all_of 0.26 0.64 3.28 3.00 3.24 4.10 215 | any_of 0.50 0.67 0.93 2.66 3.07 3.60 216 | none_of 0.36 0.63 0.61 2.06 2.30 2.85 217 | for_each 0.17 0.47 0.47 0.97 0.99 1.22 218 | find 0.25 0.48 0.89 2.34 2.43 3.10 219 | find_end 0.09 0.24 0.42 1.01 1.16 1.36 220 | find_first_of 0.58 1.26 2.80 3.99 4.82 5.09 221 | adjacent_find 0.41 0.61 1.16 2.70 2.39 2.58 222 | count 0.12 0.40 0.47 1.41 1.64 2.32 223 | mismatch 0.36 0.27 0.66 1.55 1.60 1.72 224 | equal 0.22 0.90 3.28 2.29 2.29 2.33 225 | search 0.36 1.18 1.70 3.48 3.88 3.93 226 | copy 0.12 0.33 0.67 0.98 1.13 1.68 227 | move 0.20 0.43 1.29 0.89 1.11 4.15 228 | swap_ranges 0.27 0.23 0.52 0.80 0.89 1.09 229 | transform 0.22 0.41 0.62 0.88 1.03 1.49 230 | replace 0.40 0.71 0.81 1.56 1.44 1.66 231 | fill 0.14 0.49 0.87 1.18 0.97 1.13 232 | reverse 0.31 0.49 0.90 1.88 1.00 0.96 233 | sort_Rnd 0.28 1.92 3.16 3.44 3.76 3.66 234 | sort_Eq 0.04 0.33 1.36 2.18 2.27 2.07 235 | sort_Asc 0.02 0.07 0.30 0.41 0.33 0.29 236 | sort_Des 0.04 0.33 1.47 2.12 1.53 2.30 237 | stable_sort 0.48 1.66 3.25 3.71 3.75 3.63 238 | is_sorted 0.40 0.51 0.68 2.15 2.43 2.80 239 | is_partitioned 0.13 0.53 0.43 1.89 2.18 2.58 240 | merge 1.00 0.45 1.15 1.12 1.23 1.28 241 | minmax_element 0.37 0.59 0.83 2.54 3.02 3.45 242 | lexicographical_compare 0.65 0.55 1.09 2.07 2.08 2.24 243 | reduce 0.51 0.81 1.08 3.12 3.93 4.21 244 | transform_reduce 0.50 0.87 1.47 3.61 4.06 4.47 245 | exclusive_scan 0.16 0.51 0.74 0.97 1.05 1.31 246 | inclusive_scan 0.17 0.28 0.82 1.01 1.06 1.33 247 | transform_exclusive_scan 0.43 0.94 2.17 2.76 2.71 2.80 248 | transform_inclusive_scan 0.35 1.04 2.29 2.87 2.79 2.85 249 | adjacent_difference 0.04 0.12 1.10 0.92 1.04 1.07 250 | uninitialized_value_construct 0.03 0.29 0.54 0.81 1.28 1.35 251 | uninitialized_copy 0.27 0.53 1.85 1.13 1.43 1.93 252 | uninitialized_move 0.29 0.68 2.01 0.89 0.91 1.08 253 | uninitialized_fill 0.33 0.59 3.08 3.16 2.95 3.10 254 | destroy 0.42 1.25 2.49 2.72 2.76 2.86 255 | ``` 256 |
257 | 258 |
259 | 2012 MacBook Pro 13" (Intel 3520M CPU, 2 cores), macOS 10.15, Xcode 12 260 | 261 | ``` 262 | 1K 10K 100K 1M 10M 100M 263 | all_of 0.32 0.47 1.28 1.23 1.83 2.04 264 | any_of 0.43 1.01 1.70 1.96 2.46 2.47 265 | none_of 0.09 0.71 1.69 1.99 2.58 3.29 266 | for_each 0.18 0.41 1.46 1.34 1.31 1.44 267 | find 0.31 0.34 1.37 1.58 2.81 3.14 268 | find_end 0.17 0.29 0.39 0.55 0.57 0.67 269 | find_first_of 0.60 1.01 0.87 2.48 3.53 3.53 270 | adjacent_find 0.06 1.45 0.93 1.53 2.68 3.47 271 | count 0.02 0.27 1.16 1.27 1.53 1.80 272 | mismatch 0.42 0.78 1.65 1.83 1.67 2.21 273 | equal 0.34 0.63 2.38 1.81 2.30 2.46 274 | search 0.83 0.68 1.67 2.55 3.60 3.63 275 | copy 0.42 0.91 1.05 1.26 1.21 1.42 276 | move 0.66 0.84 1.31 1.21 1.27 1.46 277 | swap_ranges 0.38 0.70 0.79 1.23 1.25 1.40 278 | transform 0.03 0.27 1.11 1.28 1.35 1.71 279 | replace 0.59 0.82 0.59 1.42 1.85 1.76 280 | fill 0.41 0.77 0.95 1.05 1.28 1.40 281 | reverse 0.43 0.62 1.81 1.42 1.32 1.22 282 | sort_Rnd 0.04 0.06 0.23 0.97 1.57 1.63 283 | sort_Eq 0.11 0.02 0.07 0.45 1.46 1.91 284 | sort_Asc 0.06 0.24 0.21 0.20 0.16 0.13 285 | sort_Des 0.09 0.49 1.44 1.77 1.55 1.90 286 | stable_sort 0.65 1.88 1.64 1.90 1.72 1.71 287 | is_sorted 0.32 0.53 1.01 1.25 1.93 1.95 288 | is_partitioned 0.25 0.47 1.14 1.21 1.64 1.93 289 | merge 0.99 0.56 1.24 1.08 1.07 1.18 290 | minmax_element 0.28 0.56 0.98 1.54 1.77 1.95 291 | lexicographical_compare 0.79 0.80 1.16 1.51 1.62 1.64 292 | reduce 0.59 0.70 1.47 1.49 2.14 2.47 293 | transform_reduce 0.61 0.49 1.85 1.56 2.13 2.43 294 | exclusive_scan 0.28 0.37 1.28 0.84 0.96 1.03 295 | inclusive_scan 0.07 0.38 0.79 0.76 0.91 1.02 296 | transform_exclusive_scan 0.56 0.85 0.91 0.99 1.14 1.13 297 | transform_inclusive_scan 0.50 0.92 1.03 1.12 1.12 1.16 298 | adjacent_difference 0.03 0.14 1.67 0.97 1.04 1.05 299 | uninitialized_value_construct 0.03 0.46 0.66 0.82 0.80 1.03 300 | uninitialized_copy 0.60 0.74 1.70 1.60 1.68 1.89 301 | uninitialized_move 0.19 1.22 1.30 1.07 1.19 1.29 302 | uninitialized_fill 0.52 1.47 2.11 2.12 2.48 2.48 303 | destroy 0.23 0.25 0.31 0.48 0.55 0.73 304 | ``` 305 |
306 | 307 |
308 | iPhone 11 Pro (Apple A13 CPU, 2P+4E cores), iOS 16, Xcode 14 309 | 310 | ``` 311 | 1K 10K 100K 1M 10M 30M 312 | all_of 0.32 0.64 1.06 1.99 1.85 1.95 313 | any_of 0.46 0.26 0.95 2.02 1.90 2.06 314 | none_of 0.40 0.43 0.72 1.69 1.77 1.80 315 | for_each 0.40 0.51 0.87 1.73 1.18 1.38 316 | find 0.03 0.15 0.79 1.86 1.71 1.78 317 | find_end 0.12 0.10 0.20 0.37 0.49 0.61 318 | find_first_of 0.65 0.73 1.43 2.62 2.81 2.85 319 | adjacent_find 0.39 0.40 0.43 1.21 1.54 1.56 320 | count 0.34 0.32 0.84 1.72 1.71 1.82 321 | mismatch 0.36 0.39 0.61 1.07 1.05 1.02 322 | equal 0.44 0.43 1.08 1.48 1.16 1.13 323 | search 0.39 0.49 1.15 1.95 2.22 2.22 324 | copy 0.02 0.08 0.57 0.66 1.21 1.39 325 | move 0.22 0.58 0.91 0.98 1.22 1.13 326 | swap_ranges 0.40 0.61 0.80 1.71 1.05 1.21 327 | transform 0.34 0.33 0.84 1.01 1.11 1.28 328 | replace 0.39 0.51 0.48 1.28 1.05 1.26 329 | fill 0.13 0.20 0.29 0.60 0.46 0.54 330 | reverse 0.26 0.27 0.69 1.43 1.04 1.02 331 | sort_Rnd 0.12 1.40 2.51 2.92 2.86 2.70 332 | sort_Eq 0.04 0.24 0.57 0.91 1.18 1.08 333 | sort_Asc 0.05 0.09 0.21 0.24 0.19 0.18 334 | sort_Des 0.04 0.24 0.58 0.93 1.25 1.09 335 | stable_sort 0.48 1.57 2.67 2.86 2.78 2.62 336 | is_sorted 0.31 0.42 0.91 2.08 1.84 1.81 337 | is_partitioned 0.06 0.45 1.02 1.98 1.76 1.77 338 | merge 1.00 0.53 1.29 1.75 1.53 1.57 339 | minmax_element 1.20 1.00 2.27 4.62 5.54 5.44 340 | lexicographical_compare 0.47 0.64 1.11 2.16 1.69 1.66 341 | reduce 0.82 0.68 1.79 3.48 3.80 3.87 342 | transform_reduce 0.67 0.92 1.69 3.47 3.80 3.81 343 | exclusive_scan 0.50 0.54 0.93 1.87 1.44 1.50 344 | inclusive_scan 0.53 0.42 0.97 1.85 1.44 1.49 345 | transform_exclusive_scan 0.18 0.72 1.35 1.45 1.20 1.15 346 | transform_inclusive_scan 0.33 0.90 1.62 1.67 1.23 1.13 347 | adjacent_difference 0.29 0.37 0.83 0.93 1.00 0.94 348 | uninitialized_value_construct 0.03 0.12 0.43 0.99 1.15 1.35 349 | uninitialized_copy 0.34 1.10 1.51 1.04 1.30 1.69 350 | uninitialized_move 0.23 0.87 1.17 1.00 1.09 1.48 351 | uninitialized_fill 0.80 1.76 2.33 3.05 2.92 2.83 352 | destroy 0.51 0.89 1.85 1.99 0.29 0.26 353 | ``` 354 |
355 | 356 | 357 | ## Running the benchmarks 358 | 359 | Run the following commands to execute the benchmarks locally: 360 | 361 | ```Shell 362 | git clone https://github.com/mikekazakov/pstld.git && \ 363 | cd pstld && \ 364 | cd test-llvm-pstl && ./bootstrap.sh && cd - && \ 365 | cd test-msvc-stl && ./bootstrap.sh && cd - && \ 366 | cmake -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release . && \ 367 | make benchmark && 368 | ./benchmark/benchmark 369 | ``` -------------------------------------------------------------------------------- /benchmark/benchmark.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Michael G. Kazakov. All rights reserved. Distributed under the MIT License. 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static constexpr size_t g_Iterations = 10; 14 | static constexpr size_t g_IterationsDiscard = 1; 15 | static constexpr size_t g_Sizes[] = {1'000, 10'000, 100'000, 1'000'000, 10'000'000, 100'000'000}; 16 | 17 | template 18 | inline void noopt(Tp const &value) 19 | { 20 | asm volatile("" : : "r,m"(value) : "memory"); 21 | } 22 | 23 | template 24 | inline void noopt(Tp &value) 25 | { 26 | asm volatile("" : "+r,m"(value) : : "memory"); 27 | } 28 | 29 | template 30 | std::chrono::steady_clock::duration measure(Setup setup, Work work, Cleanup cleanup) 31 | { 32 | std::array runs; 33 | for( size_t i = 0; i != g_Iterations; ++i ) { 34 | setup(); 35 | const auto start = std::chrono::steady_clock::now(); 36 | work(); 37 | const auto end = std::chrono::steady_clock::now(); 38 | cleanup(); 39 | runs[i] = end - start; 40 | } 41 | std::sort(runs.begin(), runs.end()); 42 | return std::accumulate(runs.begin() + g_IterationsDiscard, 43 | runs.end() - g_IterationsDiscard, 44 | std::chrono::steady_clock::duration{}); 45 | } 46 | 47 | template 48 | std::chrono::steady_clock::duration measure(Setup setup, Work work) 49 | { 50 | return measure(setup, work, [] {}); 51 | } 52 | 53 | namespace benchmarks { 54 | 55 | template 56 | struct all_of { // 25.6.1 57 | auto operator()(size_t size) 58 | { 59 | std::vector v; 60 | return measure( 61 | [&] { v = std::vector(size, 42.); }, 62 | [&] { 63 | noopt(std::all_of(ExPo{}, v.begin(), v.end(), [](auto e) { return e < 50.; })); 64 | }); 65 | } 66 | }; 67 | 68 | template 69 | struct any_of { // 25.6.2 70 | auto operator()(size_t size) 71 | { 72 | std::vector v; 73 | return measure( 74 | [&] { v = std::vector(size, 42.); }, 75 | [&] { 76 | noopt(std::any_of(ExPo{}, v.begin(), v.end(), [](auto e) { return e > 50.; })); 77 | }); 78 | } 79 | }; 80 | 81 | template 82 | struct none_of { // 25.6.3 83 | auto operator()(size_t size) 84 | { 85 | std::vector v; 86 | return measure( 87 | [&] { v = std::vector(size, 42.); }, 88 | [&] { 89 | noopt(std::none_of(ExPo{}, v.begin(), v.end(), [](auto e) { return e > 50.; })); 90 | }); 91 | } 92 | }; 93 | 94 | template 95 | struct for_each { // 25.6.4 96 | auto operator()(size_t size) 97 | { 98 | std::vector v; 99 | return measure([&] { v = std::vector(size, 42.); }, 100 | [&] { 101 | std::for_each(ExPo{}, v.begin(), v.end(), [](auto &e) { e += 1.; }); 102 | noopt(v); 103 | }); 104 | } 105 | }; 106 | 107 | template 108 | struct find { // 25.6.5 109 | auto operator()(size_t size) 110 | { 111 | std::vector v; 112 | return measure([&] { v = std::vector(size, 42.); }, 113 | [&] { noopt(std::find(ExPo{}, v.begin(), v.end(), 50.)); }); 114 | } 115 | }; 116 | 117 | template 118 | struct find_end { // 25.6.6 119 | auto operator()(size_t size) 120 | { 121 | std::vector v1, v2{42., 42., 42., 43.}; 122 | return measure( 123 | [&] { v1 = std::vector(size, 42.); }, 124 | [&] { noopt(std::find_end(ExPo{}, v1.begin(), v1.end(), v2.begin(), v2.end())); }); 125 | } 126 | }; 127 | 128 | template 129 | struct find_first_of { // 25.6.7 130 | auto operator()(size_t size) 131 | { 132 | std::vector v1, v2{43., 44., 45., 46.}; 133 | return measure( 134 | [&] { v1 = std::vector(size, 42.); }, 135 | [&] { noopt(std::find_first_of(ExPo{}, v1.begin(), v1.end(), v2.begin(), v2.end())); }); 136 | } 137 | }; 138 | 139 | template 140 | struct adjacent_find { // 25.6.8 141 | auto operator()(size_t size) 142 | { 143 | std::vector v; 144 | return measure( 145 | [&] { 146 | v = std::vector(size); 147 | std::iota(v.begin(), v.end(), 1.); 148 | }, 149 | [&] { noopt(std::adjacent_find(ExPo{}, v.begin(), v.end())); }); 150 | } 151 | }; 152 | 153 | template 154 | struct count { // 25.6.9 155 | auto operator()(size_t size) 156 | { 157 | std::vector v; 158 | return measure([&] { v = std::vector(size, 42.); }, 159 | [&] { noopt(std::count(ExPo{}, v.begin(), v.end(), 42.)); }); 160 | } 161 | }; 162 | 163 | template 164 | struct mismatch { // 25.6.10 165 | auto operator()(size_t size) 166 | { 167 | std::vector v1, v2; 168 | return measure([&] { v1 = v2 = std::vector(size, 42.); }, 169 | [&] { noopt(std::mismatch(ExPo{}, v1.begin(), v1.end(), v2.begin())); }); 170 | } 171 | }; 172 | 173 | template 174 | struct equal { // 25.6.11 175 | auto operator()(size_t size) 176 | { 177 | std::vector v1, v2; 178 | return measure([&] { v1 = v2 = std::vector(size, 42.); }, 179 | [&] { noopt(std::equal(ExPo{}, v1.begin(), v1.end(), v2.begin())); }); 180 | } 181 | }; 182 | 183 | template 184 | struct search { // 25.6.13 185 | auto operator()(size_t size) 186 | { 187 | std::vector v1, v2{42., 42., 42., 43.}; 188 | return measure( 189 | [&] { v1 = std::vector(size, 42.); }, 190 | [&] { noopt(std::search(ExPo{}, v1.begin(), v1.end(), v2.begin(), v2.end())); }); 191 | } 192 | }; 193 | 194 | template 195 | struct copy { // 25.7.1 196 | auto operator()(size_t size) 197 | { 198 | std::vector v1, v2; 199 | return measure( 200 | [&] { 201 | v1 = std::vector(size, 42.); 202 | v2 = std::vector(size); 203 | }, 204 | [&] { 205 | std::copy(ExPo{}, v1.begin(), v1.end(), v2.begin()); 206 | noopt(v2); 207 | }); 208 | } 209 | }; 210 | 211 | template 212 | struct move { // 25.7.2 213 | auto operator()(size_t size) 214 | { 215 | std::vector v1; 216 | std::vector v2; 217 | return measure( 218 | [&] { 219 | v1 = std::vector(size, "Small string"); 220 | v2 = std::vector(size); 221 | }, 222 | [&] { 223 | std::move(ExPo{}, v1.begin(), v1.end(), v2.begin()); 224 | noopt(v2); 225 | }); 226 | } 227 | }; 228 | 229 | template 230 | struct swap_ranges { // 25.7.3 231 | auto operator()(size_t size) 232 | { 233 | std::vector v1, v2; 234 | return measure( 235 | [&] { 236 | v1 = std::vector(size, 42.); 237 | v2 = std::vector(size, 43.); 238 | }, 239 | [&] { 240 | std::swap_ranges(ExPo{}, v1.begin(), v1.end(), v2.begin()); 241 | noopt(v2); 242 | }); 243 | } 244 | }; 245 | 246 | template 247 | struct transform { // 25.7.4 248 | auto operator()(size_t size) 249 | { 250 | std::vector v1, v2, v3; 251 | return measure( 252 | [&] { 253 | v1 = std::vector(size, 42.); 254 | v2 = std::vector(size, 71.); 255 | v3 = std::vector(size); 256 | }, 257 | [&] { 258 | std::transform( 259 | ExPo{}, v1.begin(), v1.end(), v2.begin(), v3.begin(), [](auto a, auto b) { 260 | return a * b; 261 | }); 262 | noopt(v3); 263 | }); 264 | } 265 | }; 266 | 267 | template 268 | struct replace { // 25.7.5 269 | auto operator()(size_t size) 270 | { 271 | std::vector v; 272 | return measure([&] { v = std::vector(size, 42.); }, 273 | [&] { 274 | std::replace(ExPo{}, v.begin(), v.end(), 42., 43.); 275 | noopt(v); 276 | }); 277 | } 278 | }; 279 | 280 | template 281 | struct fill { // 25.7.6 282 | auto operator()(size_t size) 283 | { 284 | std::vector v; 285 | return measure([&] { v = std::vector(size); }, 286 | [&] { 287 | std::fill(ExPo{}, v.begin(), v.end(), 42.); 288 | noopt(v); 289 | }); 290 | } 291 | }; 292 | 293 | template 294 | struct reverse { // 25.7.10 295 | auto operator()(size_t size) 296 | { 297 | std::vector v; 298 | return measure( 299 | [&] { 300 | v = std::vector(size); 301 | std::iota(v.begin(), v.end(), 0.); 302 | }, 303 | [&] { 304 | std::reverse(ExPo{}, v.begin(), v.end()); 305 | noopt(v); 306 | }); 307 | } 308 | }; 309 | 310 | template 311 | struct sort_Rnd { // 25.8.2.1, semi-random input 312 | auto operator()(size_t size) 313 | { 314 | std::vector v; 315 | return measure( 316 | [&] { 317 | std::mt19937 mt{42}; 318 | std::uniform_real_distribution dist{0., 1.}; 319 | v = std::vector(size); 320 | std::generate(std::begin(v), std::end(v), [&dist, &mt] { return dist(mt); }); 321 | }, 322 | [&] { 323 | std::sort(ExPo{}, v.begin(), v.end()); 324 | noopt(v); 325 | }); 326 | } 327 | }; 328 | 329 | template 330 | struct sort_Eq { // 25.8.2.1, equal input 331 | auto operator()(size_t size) 332 | { 333 | std::vector v; 334 | return measure([&] { v = std::vector(size, 42.); }, 335 | [&] { 336 | std::sort(ExPo{}, v.begin(), v.end()); 337 | noopt(v); 338 | }); 339 | } 340 | }; 341 | 342 | template 343 | struct sort_Asc { // 25.8.2.1, ascending 344 | auto operator()(size_t size) 345 | { 346 | std::vector v; 347 | return measure( 348 | [&] { 349 | v = std::vector(size); 350 | std::iota(v.begin(), v.end(), 0.); 351 | }, 352 | [&] { 353 | std::sort(ExPo{}, v.begin(), v.end()); 354 | noopt(v); 355 | }); 356 | } 357 | }; 358 | 359 | template 360 | struct sort_Des { // 25.8.2.1, descending 361 | auto operator()(size_t size) 362 | { 363 | std::vector v; 364 | return measure( 365 | [&] { 366 | v = std::vector(size); 367 | std::generate( 368 | v.begin(), v.end(), [v = std::numeric_limits::max()]() mutable { 369 | return v -= 1.; 370 | }); 371 | }, 372 | [&] { 373 | std::sort(ExPo{}, v.begin(), v.end()); 374 | noopt(v); 375 | }); 376 | } 377 | }; 378 | 379 | template 380 | struct stable_sort { // 25.8.2.2, semi-random input 381 | auto operator()(size_t size) 382 | { 383 | std::vector v; 384 | return measure( 385 | [&] { 386 | std::mt19937 mt{42}; 387 | std::uniform_real_distribution dist{0., 1.}; 388 | v = std::vector(size); 389 | std::generate(std::begin(v), std::end(v), [&dist, &mt] { return dist(mt); }); 390 | }, 391 | [&] { 392 | std::stable_sort(ExPo{}, v.begin(), v.end()); 393 | noopt(v); 394 | }); 395 | } 396 | }; 397 | 398 | template 399 | struct is_sorted { // 25.8.2.5 400 | auto operator()(size_t size) 401 | { 402 | std::vector v; 403 | return measure([&] { v = std::vector(size, 42.); }, 404 | [&] { noopt(std::is_sorted(ExPo{}, v.begin(), v.end())); }); 405 | } 406 | }; 407 | 408 | template 409 | struct is_partitioned { // 25.8.5 410 | auto operator()(size_t size) 411 | { 412 | std::vector v; 413 | return measure([&] { v = std::vector(size, 42.); }, 414 | [&] { 415 | noopt(std::is_partitioned( 416 | ExPo{}, v.begin(), v.end(), [](double v) { return v < 50.; })); 417 | }); 418 | } 419 | }; 420 | 421 | template 422 | struct merge { // 25.8.6 423 | auto operator()(size_t size) 424 | { 425 | std::vector v1, v2, v3; 426 | return measure( 427 | [&] { 428 | v1 = std::vector(size); 429 | v2 = std::vector(size); 430 | v3 = std::vector(size + size); 431 | std::iota(v1.begin(), v1.end(), 0.); 432 | std::iota(v2.begin(), v2.end(), 0.); 433 | }, 434 | [&] { 435 | noopt(std::merge(ExPo{}, v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin())); 436 | }); 437 | } 438 | }; 439 | 440 | template 441 | struct minmax_element { // 25.8.9 442 | auto operator()(size_t size) 443 | { 444 | std::vector v; 445 | return measure([&] { v = std::vector(size, 42.); }, 446 | [&] { noopt(std::minmax_element(ExPo{}, v.begin(), v.end())); }); 447 | } 448 | }; 449 | 450 | template 451 | struct lexicographical_compare { // 25.8.11 452 | auto operator()(size_t size) 453 | { 454 | std::vector v1, v2; 455 | return measure([&] { v1 = v2 = std::vector(size, 42.); }, 456 | [&] { 457 | noopt(std::lexicographical_compare( 458 | ExPo{}, v1.begin(), v1.end(), v2.begin(), v2.end())); 459 | }); 460 | } 461 | }; 462 | 463 | template 464 | struct reduce { // 25.10.4 465 | auto operator()(size_t size) 466 | { 467 | std::vector v; 468 | return measure([&] { v = std::vector(size, 42.); }, 469 | [&] { noopt(std::reduce(ExPo{}, v.begin(), v.end())); }); 470 | } 471 | }; 472 | 473 | template 474 | struct transform_reduce { // 25.10.6 475 | auto operator()(size_t size) 476 | { 477 | std::vector v; 478 | return measure( 479 | [&] { v = std::vector(size, 42.); }, 480 | [&] { 481 | noopt(std::transform_reduce( 482 | ExPo{}, v.begin(), v.end(), 0., std::plus<>{}, [](auto d) { return d + 1.; })); 483 | }); 484 | } 485 | }; 486 | 487 | template 488 | struct exclusive_scan { // 25.10.8 489 | auto operator()(size_t size) 490 | { 491 | std::vector v1, v2; 492 | return measure( 493 | [&] { 494 | v1 = std::vector(size, 1.01); 495 | v2 = std::vector(size); 496 | }, 497 | [&] { 498 | noopt(std::exclusive_scan( 499 | ExPo{}, v1.begin(), v1.end(), v2.begin(), 1.02, std::multiplies<>{})); 500 | }); 501 | } 502 | }; 503 | 504 | template 505 | struct inclusive_scan { // 25.10.9 506 | auto operator()(size_t size) 507 | { 508 | std::vector v1, v2; 509 | return measure( 510 | [&] { 511 | v1 = std::vector(size, 1.01); 512 | v2 = std::vector(size); 513 | }, 514 | [&] { 515 | noopt(std::inclusive_scan( 516 | ExPo{}, v1.begin(), v1.end(), v2.begin(), std::multiplies<>{}, 1.02)); 517 | }); 518 | } 519 | }; 520 | 521 | template 522 | struct transform_exclusive_scan { // 25.10.10 523 | auto operator()(size_t size) 524 | { 525 | std::vector v1, v2; 526 | return measure( 527 | [&] { 528 | v1 = std::vector(size, 1.01); 529 | v2 = std::vector(size); 530 | }, 531 | [&] { 532 | noopt(std::transform_exclusive_scan(ExPo{}, 533 | v1.begin(), 534 | v1.end(), 535 | v2.begin(), 536 | 1.02, 537 | std::multiplies<>{}, 538 | [](double v) { return pow(v, 1.01); })); 539 | }); 540 | } 541 | }; 542 | 543 | template 544 | struct transform_inclusive_scan { // 25.10.11 545 | auto operator()(size_t size) 546 | { 547 | std::vector v1, v2; 548 | return measure( 549 | [&] { 550 | v1 = std::vector(size, 1.01); 551 | v2 = std::vector(size); 552 | }, 553 | [&] { 554 | noopt(std::transform_inclusive_scan( 555 | ExPo{}, 556 | v1.begin(), 557 | v1.end(), 558 | v2.begin(), 559 | std::multiplies<>{}, 560 | [](double v) { return pow(v, 1.01); }, 561 | 1.02)); 562 | }); 563 | } 564 | }; 565 | 566 | template 567 | struct adjacent_difference { // 25.10.12 568 | auto operator()(size_t size) 569 | { 570 | std::vector v1, v2; 571 | return measure( 572 | [&] { 573 | v1 = v2 = std::vector(size); 574 | std::iota(v1.begin(), v1.end(), 0.); 575 | }, 576 | [&] { 577 | std::adjacent_difference(ExPo{}, v1.begin(), v1.end(), v2.begin()); 578 | noopt(v2); 579 | }); 580 | } 581 | }; 582 | 583 | template 584 | struct uninitialized_value_construct { // 25.11.4 585 | auto operator()(size_t size) 586 | { 587 | std::unique_ptr mem; 588 | return measure([&] { mem = std::make_unique(sizeof(std::string) * size); }, 589 | [&] { 590 | std::uninitialized_value_construct( 591 | ExPo{}, (std::string *)mem.get(), (std::string *)mem.get() + size); 592 | noopt(mem); 593 | }, 594 | [&] { 595 | std::destroy((std::string *)mem.get(), (std::string *)mem.get() + size); 596 | noopt(mem); 597 | }); 598 | } 599 | }; 600 | 601 | template 602 | struct uninitialized_copy { // 25.11.5 603 | auto operator()(size_t size) 604 | { 605 | std::vector src = std::vector(size, "Small string"); 606 | std::unique_ptr mem; 607 | return measure([&] { mem = std::make_unique(sizeof(std::string) * size); }, 608 | [&] { 609 | std::uninitialized_copy( 610 | ExPo{}, src.begin(), src.end(), (std::string *)mem.get()); 611 | noopt(mem); 612 | }, 613 | [&] { 614 | std::destroy((std::string *)mem.get(), (std::string *)mem.get() + size); 615 | noopt(mem); 616 | }); 617 | } 618 | }; 619 | 620 | template 621 | struct uninitialized_move { // 25.11.6 622 | auto operator()(size_t size) 623 | { 624 | std::vector src; 625 | std::unique_ptr mem; 626 | return measure( 627 | [&] { 628 | src = std::vector(size, "Small string"); 629 | mem = std::make_unique(sizeof(std::string) * size); 630 | }, 631 | [&] { 632 | std::uninitialized_move(ExPo{}, src.begin(), src.end(), (std::string *)mem.get()); 633 | noopt(mem); 634 | }, 635 | [&] { 636 | std::destroy((std::string *)mem.get(), (std::string *)mem.get() + size); 637 | noopt(mem); 638 | }); 639 | } 640 | }; 641 | 642 | template 643 | struct uninitialized_fill { // 25.11.7 644 | auto operator()(size_t size) 645 | { 646 | std::unique_ptr mem; 647 | return measure([&] { mem = std::make_unique(sizeof(std::string) * size); }, 648 | [&] { 649 | std::uninitialized_fill(ExPo{}, 650 | (std::string *)mem.get(), 651 | (std::string *)mem.get() + size, 652 | "Small string"); 653 | noopt(mem); 654 | }, 655 | [&] { 656 | std::destroy((std::string *)mem.get(), (std::string *)mem.get() + size); 657 | noopt(mem); 658 | }); 659 | } 660 | }; 661 | 662 | template 663 | struct destroy { // 25.11.9 664 | auto operator()(size_t size) 665 | { 666 | std::unique_ptr mem; 667 | return measure( 668 | [&] { 669 | mem = std::make_unique(sizeof(std::string) * size); 670 | std::uninitialized_fill_n( 671 | (std::string *)mem.get(), size, "Hello from a definitely non-SBO string!"); 672 | }, 673 | [&] { 674 | std::destroy(ExPo{}, (std::string *)mem.get(), (std::string *)mem.get() + size); 675 | noopt(mem); 676 | }); 677 | } 678 | }; 679 | 680 | template 681 | static std::string demangle() 682 | { 683 | const char *name = typeid(T).name(); 684 | char s[1024]; 685 | size_t len = sizeof(s); 686 | int status; 687 | std::string norm = abi::__cxa_demangle(name, s, &len, &status); 688 | norm.erase(0, std::string_view{"benchmarks::"}.length()); 689 | norm.erase(norm.find_first_of('<')); 690 | return norm; 691 | } 692 | 693 | } // namespace benchmarks 694 | 695 | struct Result { 696 | std::string name; 697 | std::array speedups; 698 | }; 699 | 700 | template