Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for using this thread pool with coroutines #14

Draft
wants to merge 36 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9d27555
Update CPM version
DeveloperPaul123 Aug 19, 2022
da80813
Add basic support for running coroutines on pool
DeveloperPaul123 Aug 19, 2022
83bec54
WIP: Add coroutine thread pool benchmark
DeveloperPaul123 Aug 19, 2022
c493f52
Add bulk enqueue function
DeveloperPaul123 Sep 13, 2022
e8b9e6d
Do not build benchmarks for style check CI
DeveloperPaul123 Sep 13, 2022
3be61ea
Add ccache support
DeveloperPaul123 Sep 15, 2022
e42fa8f
Try to correct errors in tests
DeveloperPaul123 Sep 15, 2022
107305e
Another CPM update
DeveloperPaul123 Sep 15, 2022
fa20ee6
Add benchmark for batched task execution
DeveloperPaul123 Sep 15, 2022
94fc466
Build fixes for coroutines and clang
DeveloperPaul123 Sep 15, 2022
691414b
Improve batched task execution and remove modulus
DeveloperPaul123 Sep 15, 2022
dceb94f
Further safety for function invocation in thread
DeveloperPaul123 Sep 15, 2022
0b50e1f
Make schedule() nodiscard
DeveloperPaul123 Sep 15, 2022
adce8c8
Documentation update
DeveloperPaul123 Sep 15, 2022
136faee
Update to Clang 14 on CI
DeveloperPaul123 Sep 15, 2022
2f71162
Formatting updates
DeveloperPaul123 Sep 15, 2022
67c4c65
Add badges to README
DeveloperPaul123 Sep 27, 2022
28c6609
use feature test macro to check feature available
Oct 2, 2022
dad69c1
Merge pull request #15 from cauliyang/master
DeveloperPaul123 Oct 5, 2022
e7dc22c
Update CPM version
DeveloperPaul123 Aug 19, 2022
bd3986d
Add basic support for running coroutines on pool
DeveloperPaul123 Aug 19, 2022
efe7386
WIP: Add coroutine thread pool benchmark
DeveloperPaul123 Aug 19, 2022
48ebc32
Add bulk enqueue function
DeveloperPaul123 Sep 13, 2022
abf4559
Do not build benchmarks for style check CI
DeveloperPaul123 Sep 13, 2022
661b54c
Add ccache support
DeveloperPaul123 Sep 15, 2022
c9b6e58
Try to correct errors in tests
DeveloperPaul123 Sep 15, 2022
8df5965
Another CPM update
DeveloperPaul123 Sep 15, 2022
463b9e8
Add benchmark for batched task execution
DeveloperPaul123 Sep 15, 2022
3d3c35e
Build fixes for coroutines and clang
DeveloperPaul123 Sep 15, 2022
6eb7c36
Improve batched task execution and remove modulus
DeveloperPaul123 Sep 15, 2022
ede27fa
Further safety for function invocation in thread
DeveloperPaul123 Sep 15, 2022
bf8d3a1
Make schedule() nodiscard
DeveloperPaul123 Sep 15, 2022
1e17b2d
Documentation update
DeveloperPaul123 Sep 15, 2022
6cea89e
Update to Clang 14 on CI
DeveloperPaul123 Sep 15, 2022
4e910b8
Formatting updates
DeveloperPaul123 Sep 15, 2022
e946dfa
Merge branch 'feature/coroutine-support' of github.com:DeveloperPaul1…
DeveloperPaul123 Oct 12, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .cmake-format
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ format:
tab_size: 4
line_width: 100
dangle_parens: true
max_pargs_hwrap: 4

parse:
additional_commands:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
pip3 install cmake_format==0.6.11 pyyaml

- name: configure
run: cmake -G Ninja -S . -B build -DTP_BUILD_EXAMPLES=OFF
run: cmake -G Ninja -S . -B build -DTP_BUILD_EXAMPLES=OFF -DTP_BUILD_BENCHMARKS=OFF

- name: check style
run: cmake --build build --target check-format
2 changes: 1 addition & 1 deletion .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- name: Set up Clang
uses: egor-tensin/setup-clang@v1
with:
version: 13
version: 14
platform: x64

- name: set up GCC
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
.vs
out
CMakeFiles
.idea
cmake-build*
16 changes: 12 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)

# ---- Project ----

# Note: update this to your new project's name and version
project(
ThreadPool
VERSION 0.4.1
Expand All @@ -19,9 +16,14 @@ if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
)
endif()

find_program(CCACHE_EXE ccache)
if(EXISTS ${CCACHE_EXE})
message(STATUS "Found ccache ${CCACHE_EXE}")
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_EXE})
endif()

# ---- Add dependencies via CPM ----
# see https://github.com/TheLartians/CPM.cmake for more info

include(cmake/CPM.cmake)

# PackageProject.cmake will be used to make our target installable
Expand Down Expand Up @@ -60,6 +62,12 @@ target_link_libraries(${PROJECT_NAME} INTERFACE Threads::Threads)

# being a cross-platform target, we enforce standards conformance on MSVC
target_compile_options(${PROJECT_NAME} INTERFACE "$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/permissive->")
target_compile_options(${PROJECT_NAME} INTERFACE "$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-fcoroutines>")

if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lc++")
endif()

target_include_directories(
${PROJECT_NAME} INTERFACE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
Expand Down
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ thread-pool

[![say thanks](https://img.shields.io/badge/Say%20Thanks-👍-1EAEDB.svg)](https://github.com/DeveloperPaul123/periodic-function/stargazers)
[![Discord](https://img.shields.io/discord/652515194572111872)](https://discord.gg/CX2ybByRnt)
![License](https://img.shields.io/github/license/DeveloperPaul123/thread-pool?color=blue)
![Release](https://img.shields.io/github/v/release/DeveloperPaul123/thread-pool)

[![Ubuntu](https://github.com/DeveloperPaul123/thread-pool/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/DeveloperPaul123/thread-pool/actions/workflows/ubuntu.yml)
[![Windows](https://github.com/DeveloperPaul123/thread-pool/actions/workflows/windows.yml/badge.svg)](https://github.com/DeveloperPaul123/thread-pool/actions/workflows/windows.yml)
Expand Down Expand Up @@ -80,12 +82,12 @@ See the `./benchmark` folder for the benchmark code. The benchmarks are set up t
Matrix sizes are all square (MxM). Each multiplication is `(MxM) * (MxM)` where `*` refers to a matrix multiplication operation. Times recorded were the best of at least 3 runs.

| Matrix Size | Number of multiplications | `std::async` time (ms) | `dp::thread_pool` time (ms) |
|:---:|:---:|:---:|:---:|
| 8 | 25,000 | 77.9 | 65.3 |
| 64 | 5,000 | 100 | 65.2 |
| 256 | 250 | 295 | 59.2 |
| 512 | 75 | 713 | 60.4 |
| 1024 | 10 | 1160 | 55.8 |
|:-----------:|:-------------------------:|:----------------------:|:---------------------------:|
| 8 | 25,000 | 77.9 | 65.3 |
| 64 | 5,000 | 100 | 65.2 |
| 256 | 250 | 295 | 59.2 |
| 512 | 75 | 713 | 60.4 |
| 1024 | 10 | 1160 | 55.8 |

## Building

Expand All @@ -105,10 +107,10 @@ cmake --build build

### Build Options

| Option | Description | Default |
|:-------|:------------|:--------:|
| `TP_BUILD_TESTS` | Turn on to build unit tests. Required for formatting build targets. | ON |
| `TP_BUILD_EXAMPLES` | Turn on to build examples | ON |
| Option | Description | Default |
|:--------------------|:--------------------------------------------------------------------|:-------:|
| `TP_BUILD_TESTS` | Turn on to build unit tests. Required for formatting build targets. | ON |
| `TP_BUILD_EXAMPLES` | Turn on to build examples | ON |

### Run clang-format

Expand Down
11 changes: 9 additions & 2 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ CPMAddPackage(
NAME benchmark
GITHUB_REPOSITORY google/benchmark
VERSION 1.6.1
OPTIONS "BENCHMARK_ENABLE_TESTING OFF"
OPTIONS "BENCHMARK_ENABLE_TESTING OFF" "CMAKE_CROSSCOMPILING ON" "HAVE_STD_REGEX OFF"
"HAVE_POSIX_REGEX OFF"
)

CPMAddPackage(
NAME cppcoro
GITHUB_REPOSITORY andreasbuhr/cppcoro
GIT_TAG 10bbcdbf2be3ad3aa56febcf4c7662d771460a99
)

if(benchmark_ADDED)
Expand All @@ -26,7 +33,7 @@ file(GLOB sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp)
file(GLOB headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)

add_executable(${PROJECT_NAME} ${headers} ${sources})
target_link_libraries(${PROJECT_NAME} benchmark ThreadPool::ThreadPool)
target_link_libraries(${PROJECT_NAME} benchmark ThreadPool::ThreadPool cppcoro)
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 20)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)

Expand Down
10 changes: 10 additions & 0 deletions benchmark/include/utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

#include <algorithm>
#include <cmath>
#include <cppcoro/task.hpp>
#include <iterator>
#include <random>
#include <span>
#include <vector>

#include "thread_pool/thread_pool.h"

inline std::size_t index(std::size_t row, std::size_t col, std::size_t width) {
return row * width + col;
}
Expand All @@ -22,6 +25,12 @@ inline void multiply_array(std::span<int> a, std::span<int> b, std::span<int> re
}
}

inline cppcoro::task<> co_multiply_array(std::span<int> a, std::span<int> b, std::span<int> result,
dp::thread_pool<>& pool) {
co_await pool.schedule();
multiply_array(a, b, result);
}

template <typename T>
using multiplication_pair = std::pair<std::vector<T>, std::vector<T>>;

Expand All @@ -30,6 +39,7 @@ template <typename T>
const std::int64_t& array_size, const std::int64_t& number_of_multiplications) {
static std::uniform_int_distribution<T> distribution(std::numeric_limits<T>::min(),
std::numeric_limits<T>::max());
// yes, predictable values
static std::default_random_engine generator{};

std::vector<multiplication_pair<T>> computations;
Expand Down
93 changes: 91 additions & 2 deletions benchmark/source/thread_pool.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#include <benchmark/benchmark.h>
#include <thread_pool/thread_pool.h>

#include <cppcoro/sync_wait.hpp>
#include <cppcoro/when_all.hpp>

#include "utilities.h"

static void BM_array_multiplication(benchmark::State& state) {
static void BM_array_multiplication_thread_pool(benchmark::State& state) {
const auto array_size = state.range(0);
const std::size_t multiplications_to_perform = state.range(1);

Expand All @@ -28,14 +31,100 @@ static void BM_array_multiplication(benchmark::State& state) {
}
}

BENCHMARK(BM_array_multiplication)
BENCHMARK(BM_array_multiplication_thread_pool)
#if defined(NDEBUG)
->Args({8, 25'000})
->Args({64, 5'000})
->Args({256, 250})
->Args({512, 75})
->Args({1024, 10})
#else
->Args({8, 50})
#endif
->Unit(benchmark::kMillisecond)
->ReportAggregatesOnly(true)
->MeasureProcessCPUTime()
->UseRealTime()
->Name("dp::thread_pool array mult");

static void BM_array_multiplication_batch_thread_pool(benchmark::State& state) {
const auto array_size = state.range(0);
const std::size_t multiplications_to_perform = state.range(1);

// generate the data
const auto computations = generate_benchmark_data<int>(array_size, multiplications_to_perform);

// create our tasks
std::vector<std::function<void()>> tasks{};
tasks.reserve(computations.size());

// task that is run on a new thread
auto thread_task = [](multiplication_pair<int> pair) {
std::vector<int> result(pair.first.size());
multiply_array(pair.first, pair.second, result);
};

for (const auto& computation : computations) {
auto task = [comp = computation, execution_task = thread_task]() { execution_task(comp); };
tasks.emplace_back(task);
}

// create our thread pool using the default size
dp::thread_pool pool{};

for (auto _ : state) {
pool.enqueue(tasks.begin(), tasks.end());
}
}

BENCHMARK(BM_array_multiplication_batch_thread_pool)
#if defined(NDEBUG)
->Args({8, 25'000})
->Args({64, 5'000})
->Args({256, 250})
->Args({512, 75})
->Args({1024, 10})
#else
->Args({8, 50})
#endif
->Unit(benchmark::kMillisecond)
->ReportAggregatesOnly(true)
->MeasureProcessCPUTime()
->UseRealTime()
->Name("dp::thread_pool batched array mult");

inline cppcoro::task<void> mult_task(multiplication_pair<int> pair, dp::thread_pool<>& pool) {
std::vector<int> result(pair.first.size());
co_await co_multiply_array(pair.first, pair.second, result, pool);
}

static void BM_array_multiplication_thread_pool_coroutine(benchmark::State& state) {
const auto array_size = state.range(0);
const std::size_t multiplications_to_perform = state.range(1);
const auto computations = generate_benchmark_data<int>(array_size, multiplications_to_perform);
dp::thread_pool pool{};
std::vector<cppcoro::task<>> tasks;
for (auto _ : state) {
for (auto mult_pair : computations) {
tasks.emplace_back(mult_task(mult_pair, pool));
}
}

cppcoro::sync_wait(cppcoro::when_all(std::move(tasks)));
}

BENCHMARK(BM_array_multiplication_thread_pool_coroutine)
#if defined(NDEBUG)
->Args({8, 25'000})
->Args({64, 5'000})
->Args({256, 250})
->Args({512, 75})
->Args({1024, 10})
#else
->Args({8, 50})
#endif
->Unit(benchmark::kMillisecond)
->ReportAggregatesOnly(true)
->MeasureProcessCPUTime()
->UseRealTime()
->Name("dp::thread_pool coroutine array mult");
2 changes: 1 addition & 1 deletion cmake/CPM.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(CPM_DOWNLOAD_VERSION 0.32.0)
set(CPM_DOWNLOAD_VERSION 0.35.6)

if(CPM_SOURCE_CACHE)
# Expand relative path. This is important if the provided path contains a tilde (~)
Expand Down