Skip to content

Commit

Permalink
Setup workflows and tests
Browse files Browse the repository at this point in the history
Fix any bugs that fail initial cross-platform tests
  • Loading branch information
alugowski committed Nov 13, 2023
1 parent cb92938 commit 964d0ac
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 50 deletions.
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
# Check for updates to GitHub Actions every week
# See https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
interval: "weekly"
93 changes: 93 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: tests

on:
push:
pull_request:

jobs:
build:
name: ${{matrix.os}} ${{ matrix.description }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
# oldest LLVM that the install-llvm-action action is able to install
description: "LLVM 5"
llvm-version: "5"

- os: ubuntu-20.04
# oldest GCC that the setup-gcc action is able to install
description: "GCC 7"
gcc-version: "7"
cmake-version: "3.18"

- os: ubuntu-latest
description: "GCC 13"
gcc-version: "13"

- os: ubuntu-latest
# default GCC, which has gcov

- os: macos-latest
# uses Apple Clang

- os: windows-latest
# uses MSVC

steps:
- uses: actions/checkout@v4

- name: Install TBB (Ubuntu)
if: contains(matrix.os, 'ubuntu')
run: sudo apt-get install -y libtbb-dev

- name: Install TBB (macOS)
if: contains(matrix.os, 'macos')
run: brew install tbb

- name: Setup GCC
uses: egor-tensin/setup-gcc@v1
if: ${{ matrix.gcc-version != '' }}
with:
version: ${{ matrix.gcc-version }}

- name: Setup LLVM and Clang
uses: KyleMayes/install-llvm-action@v1
if: ${{ matrix.llvm-version != '' }}
with:
version: ${{ matrix.llvm-version }}
env: true

- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1
if: ${{ matrix.cmake-version != '' }}
with:
cmake-version: ${{ matrix.cmake-version }}

- name: Build and Test
run: |
cmake -S . -B build/ -DCMAKE_BUILD_TYPE=Debug -DPOOLSTL_TEST=ON -DPOOLSTL_TEST_COVERAGE=ON
cmake --build build/ --config Debug
cd build/tests
ctest -C Debug --output-on-failure --verbose
cd ../..
find .
shell: bash

- name: Benchmark
if: ${{ matrix.gcc-version != '7' }}
run: |
cmake -S . -B bench_build/ -DCMAKE_BUILD_TYPE=Release -DPOOLSTL_BENCH=ON
cmake --build bench_build/ --config Release
cd bench_build/benchmark/
./poolstl_bench || ./Release/poolstl_bench.exe
shell: bash

- name: Upload Coverage to Codecov
if: contains(matrix.os, 'ubuntu')
uses: codecov/codecov-action@v3
with:
gcov: true
gcov_include: include/*
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
[![tests](https://github.com/alugowski/poolSTL/actions/workflows/tests.yml/badge.svg)](https://github.com/alugowski/poolSTL/actions/workflows/tests.yml)
[![codecov](https://codecov.io/gh/alugowski/poolSTL/branch/main/graph/badge.svg?token=)](https://codecov.io/gh/alugowski/poolSTL)

# poolSTL

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

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.
Expand Down
8 changes: 4 additions & 4 deletions benchmark/algorithm_bench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ void for_each(benchmark::State& state) {

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;});
std::for_each(policy<ExecutionPolicy>::get(), values.begin(), values.end(), [&](auto v) { slow(); dest[v] = v; });
} else {
std::for_each(values.begin(), values.end(), [&](auto v) {dest[v] = v;});
std::for_each(values.begin(), values.end(), [&](auto v) {slow(); dest[v] = v;});
}
benchmark::DoNotOptimize(dest);
benchmark::ClobberMemory();
Expand All @@ -69,9 +69,9 @@ void transform(benchmark::State& state) {

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; });
std::transform(policy<ExecutionPolicy>::get(), values.begin(), values.end(), dest.begin(), [&](auto v) { slow(); return v; });
} else {
std::transform(values.begin(), values.end(), dest.begin(), [&](auto v) { return v; });
std::transform(values.begin(), values.end(), dest.begin(), [&](auto v) { slow(); return v; });
}
benchmark::DoNotOptimize(dest);
benchmark::ClobberMemory();
Expand Down
10 changes: 10 additions & 0 deletions benchmark/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,14 @@ bool static_init() {
}
static bool initialized = static_init();

void slow() {
int sum = 0;

for (int i = 0; i < 10; ++i) {
sum += i*31 + sum;
}

benchmark::DoNotOptimize(sum);
}

BENCHMARK_MAIN();
6 changes: 6 additions & 0 deletions benchmark/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#define POOLSTL_BENCH_UTILS_HPP

#include <algorithm>
#ifdef POOLSTL_BENCH_STD_PAR
#include <execution>
#endif
#include <numeric>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -40,11 +42,13 @@ template <> struct policy<poolstl::execution::par_pool> {
return poolstl::execution::par_pool(pool);
};
};
#ifdef POOLSTL_BENCH_STD_PAR
template <> struct policy<std_par> {
constexpr static const std::execution::parallel_policy& get() {
return std::execution::par;
};
};
#endif

template <typename T=int>
std::vector<T> iota_vector(size_t size, T init=0) {
Expand All @@ -53,4 +57,6 @@ std::vector<T> iota_vector(size_t size, T init=0) {
return ret;
}

void slow();

#endif
8 changes: 4 additions & 4 deletions include/poolstl/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ namespace std {
auto chunk_size = get_chunk_size(first, last, pool(policy).get_num_threads());

while (first < last) {
InputIt loop_end = ::std::min(first + chunk_size, last);
InputIt loop_end = first + ::std::min(chunk_size, std::distance(first, last));

futures.emplace_back(pool(policy).submit([&](InputIt chunk_start, InputIt chunk_stop) {
for (; chunk_start != chunk_stop; ++chunk_start) {
f(*chunk_start);
futures.emplace_back(pool(policy).submit([&f](InputIt chunk_first, InputIt chunk_last) {
for (; chunk_first != chunk_last; ++chunk_first) {
f(*chunk_first);
}
}, first, loop_end));

Expand Down
20 changes: 9 additions & 11 deletions include/poolstl/execution
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,24 @@ namespace poolstl {
}

template <typename ExecutionPolicy>
ttp::task_thread_pool& pool(ExecutionPolicy& policy) {
if constexpr (ExecutionPolicy::use_default_pool()) {
return *get_default_pool();
} else {
return policy.pool;
}
ttp::task_thread_pool& pool(ExecutionPolicy&) {
return *get_default_pool();
}

template <>
inline ttp::task_thread_pool& pool<par_pool>(par_pool& policy) {
return policy.pool;
}

template <typename T> struct is_poolstl_execution_policy : std::false_type {};
template <> struct is_poolstl_execution_policy<::poolstl::execution::parallel_policy> : std::true_type {};
template <> struct is_poolstl_execution_policy<::poolstl::execution::par_pool> : std::true_type {};

template <class T>
inline constexpr bool is_poolstl_execution_policy_v = is_poolstl_execution_policy<T>::value;

template <class ExecutionPolicy, class Tp>
using enable_if_poolstl_execution_policy =
typename std::enable_if<
is_poolstl_execution_policy_v<
typename std::remove_cv<typename std::remove_reference<ExecutionPolicy>::type>::type>,
is_poolstl_execution_policy<
typename std::remove_cv<typename std::remove_reference<ExecutionPolicy>::type>::type>::value,
Tp>::type;
}
}
Expand Down
22 changes: 15 additions & 7 deletions include/poolstl/internal/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,24 @@
#define POOLSTL_VERSION_PATCH 0

#include <cstddef>
#include <iterator>

namespace poolstl::internal {
namespace poolstl {
namespace internal {

template<typename Iterator>
std::size_t get_chunk_size(Iterator first, Iterator last, unsigned int num_threads) {
std::size_t num_steps = (last - first);
auto remainder = num_steps % num_threads;
return (num_steps / num_threads) + (remainder > 0 ? 1 : 0);
}
inline std::size_t get_chunk_size(std::size_t num_steps, unsigned int num_threads) {
auto remainder = num_steps % num_threads;
return (num_steps / num_threads) + (remainder > 0 ? 1 : 0);
}

template<typename Iterator>
typename std::iterator_traits<Iterator>::difference_type
get_chunk_size(Iterator first, Iterator last, unsigned int num_threads) {
using diff_t = typename std::iterator_traits<Iterator>::difference_type;
std::size_t num_steps = std::distance(first, last);
return static_cast<diff_t>(get_chunk_size(num_steps, num_threads));
}
}
}

#endif
11 changes: 10 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.3.2
GIT_TAG v3.4.0
GIT_SHALLOW TRUE
EXCLUDE_FROM_ALL
)
Expand All @@ -35,4 +35,13 @@ include(Catch)

add_executable(poolstl_test poolstl_test.cpp)
target_link_libraries(poolstl_test PRIVATE Catch2::Catch2WithMain poolSTL::poolSTL)
target_compile_definitions(poolstl_test PRIVATE CATCH_CONFIG_FAST_COMPILE)
# Catch2 requires C++14
target_compile_features(poolstl_test PUBLIC cxx_std_14)

catch_discover_tests(poolstl_test)

# Dedicated target to ensure C++11 builds work
add_executable(cpp11_test cpp11_test.cpp)
target_link_libraries(cpp11_test PUBLIC poolSTL::poolSTL)
target_compile_features(cpp11_test PUBLIC cxx_std_11)
17 changes: 17 additions & 0 deletions tests/cpp11_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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 <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.cend(), [](int x) {
std::cout << x << " ";
});
std::cout << std::endl;
return 0;
}
4 changes: 2 additions & 2 deletions tests/poolstl_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#include "utils.hpp"

namespace ttp = task_thread_pool;
using it = i_iter<int>;

TEST_CASE("for_each", "[alg]") {
std::atomic<int> sum{0};
Expand All @@ -40,8 +39,9 @@ TEST_CASE("for_each", "[alg]") {
TEST_CASE("default_pool", "[execution]") {
std::atomic<int> sum{0};
for (auto num_iters : test_arr_sizes) {
auto v = iota_vector(num_iters);
sum = 0;
std::for_each(poolstl::par, it(0), it(num_iters), [&](auto) { ++sum; });
std::for_each(poolstl::par, v.cbegin(), v.cend(), [&](auto) { ++sum; });
REQUIRE(sum == num_iters);
}
}
20 changes: 0 additions & 20 deletions tests/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,6 @@
constexpr std::array<int, 6> test_thread_counts = {0, 1, 2, 3, 5, 10};
constexpr std::array<int, 14> test_arr_sizes = {0, 1, 2, 3, 4, 8, 9, 10, 11, 20, 23, 77, 101};

template<typename T>
struct i_iter {
T value;

explicit i_iter(T v) : value(v) {}

T operator*() const { return value; }

T operator++() { return ++value; }

template<typename U>
i_iter operator+(const U &other) { return i_iter<T>(value + other); }

T operator-(const i_iter<T> &other) const { return value - other.value; }

bool operator<(const i_iter<T> &other) const { return value < other.value; }
bool operator==(const i_iter<T> &other) const { return value == other.value; }
bool operator!=(const i_iter<T> &other) const { return value != other.value; }
};

template <typename T=int>
std::vector<T> iota_vector(size_t size, T init=0) {
std::vector<T> ret(size);
Expand Down

0 comments on commit 964d0ac

Please sign in to comment.