Skip to content

Commit

Permalink
Initial add
Browse files Browse the repository at this point in the history
  • Loading branch information
alugowski committed Nov 13, 2023
1 parent d6f77a6 commit cb92938
Show file tree
Hide file tree
Showing 22 changed files with 1,292 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.idea/

cmake-build-*/

# Prerequisites
*.d

Expand Down
91 changes: 91 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
cmake_minimum_required(VERSION 3.12...3.26)

# Read the version from poolstl.hpp
set(VERSION_HEADER_FILE include/poolstl/internal/utils.hpp)
file(STRINGS ${VERSION_HEADER_FILE} VERSION_MAJOR_STR REGEX "define .*_VERSION_MAJOR")
file(STRINGS ${VERSION_HEADER_FILE} VERSION_MINOR_STR REGEX "define .*_VERSION_MINOR")
file(STRINGS ${VERSION_HEADER_FILE} VERSION_PATCH_STR REGEX "define .*_VERSION_PATCH")
string(REGEX MATCH "[0-9]+" VERSION_MAJOR ${VERSION_MAJOR_STR})
string(REGEX MATCH "[0-9]+" VERSION_MINOR ${VERSION_MINOR_STR})
string(REGEX MATCH "[0-9]+" VERSION_PATCH ${VERSION_PATCH_STR})

project(poolSTL VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" LANGUAGES CXX)

# Main
add_library(poolSTL INTERFACE)
# add alias so the project can be used with add_subdirectory
add_library(poolSTL::poolSTL ALIAS poolSTL)

include(GNUInstallDirs)

target_include_directories(
${PROJECT_NAME}
INTERFACE
$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)

set(HEADER_FILES
include/poolstl/poolSTL.hpp
include/poolstl/algorithm
include/poolstl/execution
include/poolstl/numeric
include/poolstl/internal/utils.hpp
include/poolstl/internal/task_thread_pool.hpp)

set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${HEADER_FILES}")

target_compile_features(poolSTL INTERFACE cxx_std_11)

# Some older compilers (eg. Clang, GCC <9) require extra link flags for C++11 threads
find_package(Threads)
target_link_libraries(poolSTL INTERFACE Threads::Threads)

###############################################

# Tests
option(POOLSTL_TEST "Enable poolSTL tests" OFF)
if(POOLSTL_TEST)
add_subdirectory(tests)
endif()

# Benchmarks
option(POOLSTL_BENCH "Enable poolSTL benchmarks" OFF)
if(POOLSTL_BENCH)
add_subdirectory(benchmark)
endif()

###############################################
# For cmake install:

include(CMakePackageConfigHelpers)

# Create version file
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)

# Create a configuration file
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/config.cmake.in"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}
PATH_VARS CMAKE_INSTALL_INCLUDEDIR)

# Install config and version files
install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME})

# Define the install targets
install(TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})

# Install the targets
install(EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME})
9 changes: 9 additions & 0 deletions LICENSE-BSD.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Copyright (C) 2023 Adam Lugowski

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 changes: 23 additions & 0 deletions LICENSE-Boost.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
21 changes: 21 additions & 0 deletions LICENSE-MIT.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Adam Lugowski

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
77 changes: 76 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,77 @@
# poolSTL
Thread pool-based parallel STL

Thread pool-based implementation of [parallel standard library algorithms](https://en.cppreference.com/w/cpp/algorithm#Execution_policies).

Those algorithms are great, but compiler support is inconsistent.
PoolSTL is a *supplement* to fill in the support gaps so we can use parallel algorithms now.

It is not meant as a full implementation, only the basics are expected to be covered.

Use this if:
* you only need the basics
* you must support a [compiler that lacks native support](https://en.cppreference.com/w/cpp/compiler_support/17) (see the "Parallel algorithms and execution policies" row)
* you cannot link against TBB for whatever reason
* the [Parallel STL](https://www.intel.com/content/www/us/en/developer/articles/guide/get-started-with-parallel-stl.html) is too heavy

## Usage

PoolSTL defines `poolstl::par` and `poolstl::par_pool` execution policies.

Example:
```c++
#include <iostream>
#include <poolstl/poolstl.hpp>

int main() {
std::vector<int> v = {0, 1, 2, 3, 4, 5};
std::for_each(poolstl::par, v.cbegin(), v.cbegin(), [](auto value) {
std::cout << value << std::endl;
} );
return 0;
}

```

### Pool control

Use `poolstl::par_pool` with your own [thread pool](https://github.com/alugowski/task-thread-pool) to have full control over thread count, thread startup, shut down, etc.:

```c++
task_thread_pool::task_thread_pool pool;

std::for_each(poolstl::par_pool(pool), v.cbegin(), v.cbegin(), [](auto) {});
```
The pool used by `poolstl::par` is managed internally by poolSTL. It is started on first use.
## Implemented algorithms
Algorithms are added on an as-needed basis. If you need one that is not present feel free to open an issue or submit a PR.
### `<algorithm>`
* [std::for_each](https://en.cppreference.com/w/cpp/algorithm/for_each)
### `<numeric>`
Note: All iterators must be random access.
## Installation
### CMake
```cmake
include(FetchContent)
FetchContent_Declare(
poolSTL
GIT_REPOSITORY https://github.com/alugowski/poolSTL
GIT_TAG main
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(poolSTL)
target_link_libraries(YOUR_TARGET poolSTL::poolSTL)
```

Alternatively copy or checkout the repo into your project and:
```cmake
add_subdirectory(poolSTL)
```
70 changes: 70 additions & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# FetchContent requires cmake >=3.11
cmake_minimum_required(VERSION 3.11...3.26)

if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra -pedantic")
set(CMAKE_CXX_FLAGS_RELEASE "-g -O3 -DNDEBUG")
endif ()

include(FetchContent)

# Add Google Benchmark
set(BENCHMARK_ENABLE_TESTING NO)

include(FetchContent)

FetchContent_Declare(
googlebenchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG origin/main
GIT_SHALLOW TRUE
EXCLUDE_FROM_ALL
)

FetchContent_MakeAvailable(googlebenchmark)

# Check for native compiler support for parallel STL
include(CheckCXXSourceCompiles)

# CMP0067: Honor language standard in try_compile() source-file signature.
# https://cmake.org/cmake/help/latest/policy/CMP0067.html
cmake_policy(SET CMP0067 NEW)
set(CMAKE_CXX_STANDARD 17)

check_cxx_source_compiles("
#include <algorithm>
#include <execution>
#include <vector>
int main(void) {
std::vector<int> values(10);
std::for_each(std::execution::par, values.begin(), values.end(), [](auto v) {});
return 0;
}
" HAVE_STD_EXECUTION_PAR)

# benchmark
add_executable(poolstl_bench
main.cpp
utils.hpp
algorithm_bench.cpp
numeric_bench.cpp
)
target_link_libraries(poolstl_bench benchmark::benchmark poolSTL::poolSTL)
target_compile_features(poolstl_bench PUBLIC cxx_std_17)

if (HAVE_STD_EXECUTION_PAR AND ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU"))
# GCC and Clang require TBB
find_package(TBB)
if (TBB_FOUND)
message("Found TBB")
target_link_libraries(poolstl_bench TBB::tbb)
else()
message("No TBB")
# No TBB means cannot use std::execution::par
unset(HAVE_STD_EXECUTION_PAR)
endif()
endif()

if (HAVE_STD_EXECUTION_PAR)
target_compile_definitions(poolstl_bench PUBLIC POOLSTL_BENCH_STD_PAR)
endif()
85 changes: 85 additions & 0 deletions benchmark/algorithm_bench.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (C) 2023 Adam Lugowski. All rights reserved.
// Use of this source code is governed by:
// the BSD 2-clause license, the MIT license, or at your choosing the BSL-1.0 license found in the LICENSE.*.txt files.
// SPDX-License-Identifier: BSD-2-Clause OR MIT OR BSL-1.0


#include <algorithm>
#include <vector>

#include <benchmark/benchmark.h>

#include "utils.hpp"
#include <poolstl/algorithm>

////////////////////////////////

template <class ExecutionPolicy>
void all_of(benchmark::State& state) {
auto values = iota_vector(arr_length);

for ([[maybe_unused]] auto _ : state) {
bool res;
if constexpr (is_policy<ExecutionPolicy>::value) {
res = std::all_of(policy<ExecutionPolicy>::get(), values.begin(), values.end(), [&](auto v) { return v >= 0; });
} else {
res = std::all_of(values.begin(), values.end(), [&](auto v) { return v >= 0; });
}
benchmark::DoNotOptimize(res);
benchmark::ClobberMemory();
}
}

BENCHMARK(all_of<seq>)->UseRealTime();
//BENCHMARK(all_of<poolstl::par>)->UseRealTime();
#ifdef POOLSTL_BENCH_STD_PAR
BENCHMARK(all_of<std_par>)->UseRealTime();
#endif

////////////////////////////////

template <class ExecutionPolicy>
void for_each(benchmark::State& state) {
auto values = iota_vector<int>(arr_length);
std::vector<int> dest(arr_length);

for ([[maybe_unused]] auto _ : state) {
if constexpr (is_policy<ExecutionPolicy>::value) {
std::for_each(policy<ExecutionPolicy>::get(), values.begin(), values.end(), [&](auto v) {dest[v] = v;});
} else {
std::for_each(values.begin(), values.end(), [&](auto v) {dest[v] = v;});
}
benchmark::DoNotOptimize(dest);
benchmark::ClobberMemory();
}
}

BENCHMARK(for_each<seq>)->UseRealTime();
BENCHMARK(for_each<poolstl_par>)->UseRealTime();
#ifdef POOLSTL_BENCH_STD_PAR
BENCHMARK(for_each<std_par>)->UseRealTime();
#endif

////////////////////////////////

template <class ExecutionPolicy>
void transform(benchmark::State& state) {
auto values = iota_vector<int>(arr_length);
std::vector<int> dest(arr_length);

for ([[maybe_unused]] auto _ : state) {
if constexpr (is_policy<ExecutionPolicy>::value) {
std::transform(policy<ExecutionPolicy>::get(), values.begin(), values.end(), dest.begin(), [&](auto v) { return v; });
} else {
std::transform(values.begin(), values.end(), dest.begin(), [&](auto v) { return v; });
}
benchmark::DoNotOptimize(dest);
benchmark::ClobberMemory();
}
}

BENCHMARK(transform<seq>)->UseRealTime();
//BENCHMARK(transform<poolstl::par>)->UseRealTime();
#ifdef POOLSTL_BENCH_STD_PAR
BENCHMARK(transform<std_par>)->UseRealTime();
#endif
Loading

0 comments on commit cb92938

Please sign in to comment.