Skip to content

Commit

Permalink
custom parallel_for implementation
Browse files Browse the repository at this point in the history
Adds concurrent_queue, thread_pool, parallel_for, function_ref, move_only_function implementations.

Removes the dependency on the parallel stl algorithms (and thus on TBB
with stdlibc++).
  • Loading branch information
KRM7 committed Sep 22, 2023
1 parent fcc102e commit 8f8267d
Show file tree
Hide file tree
Showing 18 changed files with 346 additions and 78 deletions.
2 changes: 0 additions & 2 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@
-readability-implicit-bool-conversion,
-readability-uppercase-literal-suffix,
-bugprone-easily-swappable-parameters,
-bugprone-exception-escape,
-cppcoreguidelines-special-member-functions,
-misc-unconventional-assign-operator,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
-misc-const-correctness,
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ jobs:
core-guidelines:
name: core-guidelines-check
runs-on: windows-2022
# Allow failures because of the internal compiler error with reference_lines.cpp
continue-on-error: true
# Disabled because of an internal compiler error in reference_lines.cpp
if: false

defaults:
run:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sanitizers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
env:
ASAN_OPTIONS: check_initialization_order=1:strict_init_order=1:detect_stack_use_after_return=1:detect_leaks=1
UBSAN_OPTIONS: print_stacktrace=1:print_summary=1
TSAN_OPTIONS: suppressions=../.tsan-supressions:external_symbolizer_path=/usr/lib/llvm-15/bin/llvm-symbolizer:verbosity=2:force_seq_cst_atomics=0
TSAN_OPTIONS: suppressions=../.tsan-supressions:external_symbolizer_path=/usr/lib/llvm-15/bin/llvm-symbolizer:verbosity=1:force_seq_cst_atomics=0

defaults:
run:
Expand Down
2 changes: 1 addition & 1 deletion src/algorithm/nd_sort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "../utility/algorithm.hpp"
#include "../utility/functional.hpp"
#include "../utility/iterators.hpp"
#include "../utility/parallel_for.hpp"
#include "../utility/thread_pool.hpp"
#include "../utility/math.hpp"
#include "../utility/utility.hpp"
#include "../utility/matrix.hpp"
Expand Down
2 changes: 1 addition & 1 deletion src/algorithm/nsga3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "../metrics/pop_stats.hpp"
#include "../utility/algorithm.hpp"
#include "../utility/functional.hpp"
#include "../utility/parallel_for.hpp"
#include "../utility/thread_pool.hpp"
#include "../utility/math.hpp"
#include "../utility/rng.hpp"
#include "../utility/utility.hpp"
Expand Down
2 changes: 1 addition & 1 deletion src/core/ga_base.impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#include "../stop_condition/stop_condition_base.hpp"
#include "../utility/algorithm.hpp"
#include "../utility/functional.hpp"
#include "../utility/parallel_for.hpp"
#include "../utility/thread_pool.hpp"
#include "../utility/scope_exit.hpp"
#include "../utility/utility.hpp"
#include <algorithm>
Expand Down
2 changes: 1 addition & 1 deletion src/metrics/pop_stats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "../population/population.hpp"
#include "../utility/algorithm.hpp"
#include "../utility/iterators.hpp"
#include "../utility/parallel_for.hpp"
#include "../utility/thread_pool.hpp"
#include "../utility/utility.hpp"
#include <vector>
#include <span>
Expand Down
2 changes: 1 addition & 1 deletion src/population/population.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace gapp::detail
#include "../utility/algorithm.hpp"
#include "../utility/functional.hpp"
#include "../utility/iterators.hpp"
#include "../utility/parallel_for.hpp"
#include "../utility/thread_pool.hpp"
#include "../utility/utility.hpp"
#include "../utility/math.hpp"
#include <algorithm>
Expand Down
9 changes: 0 additions & 9 deletions src/utility/algorithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,15 +328,6 @@ namespace gapp::detail
container.erase(last, container.end());
}

template<std::integral T>
constexpr void increment_mod(T& value, T mod)
{
GAPP_ASSERT(mod > 0);
GAPP_ASSERT(0 <= value && value < mod);

value = (value + 1 == mod) ? T(0) : value + 1;
}

} // namespace gapp::detail

#endif // !GA_UTILITY_ALGORITHM_HPP
63 changes: 63 additions & 0 deletions src/utility/concurrent_queue.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* Copyright (c) 2023 Krisztián Rugási. Subject to the MIT License. */

#ifndef GA_UTILITY_CONCURRENT_QUEUE_HPP
#define GA_UTILITY_CONCURRENT_QUEUE_HPP

#include "spinlock.hpp"
#include <condition_variable>
#include <mutex>
#include <deque>
#include <optional>
#include <type_traits>

namespace gapp::detail
{
template<typename T>
class concurrent_queue
{
public:
template<typename... Args>
[[nodiscard]] bool emplace(Args&&... args)
{
std::scoped_lock lock{ queue_lock_ };
if (is_closed_) return false;
queue_.emplace_back(std::forward<Args>(args)...);
queue_cv_.notify_one();
return true;
}

[[nodiscard]] std::optional<T> take() noexcept(std::is_nothrow_move_constructible_v<T>)
{
std::unique_lock lock{ queue_lock_ };
queue_cv_.wait(lock, [&]() noexcept { return !queue_.empty() || is_closed_; });

if (is_closed_ && queue_.empty()) return {};

T elem = std::move(queue_.front());
queue_.pop_front();
return elem;
}

void close() noexcept
{
std::scoped_lock lock{ queue_lock_ };
is_closed_ = true;
queue_cv_.notify_all();
}

[[nodiscard]] bool closed() const noexcept
{
std::scoped_lock lock{ queue_lock_ };
return is_closed_;
}

private:
std::deque<T> queue_;
mutable detail::spinlock queue_lock_;
std::condition_variable_any queue_cv_;
bool is_closed_ = false;
};

} // namespace gapp::detail

#endif // !GA_UTILITY_CONCURRENT_QUEUE_HPP
116 changes: 113 additions & 3 deletions src/utility/functional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@

#include "type_traits.hpp"
#include <functional>
#include <bit>
#include <array>
#include <vector>
#include <cmath>
#include <utility>
#include <memory>
#include <limits>
#include <type_traits>
#include <concepts>
#include <utility>
#include <cmath>
#include <cassert>


namespace gapp
{
template<auto F>
Expand Down Expand Up @@ -101,6 +102,7 @@ namespace gapp::detail
return flat;
}


template<typename T>
constexpr auto multiply_by(const T& multiplier)
noexcept(std::is_nothrow_copy_constructible_v<T>)
Expand Down Expand Up @@ -236,6 +238,114 @@ namespace gapp::detail
return container[idx];
};
}


template<typename...>
class function_ref;

template<typename Ret, typename... Args>
class function_ref<Ret(Args...)>
{
public:
template<typename Callable>
requires(!std::is_same_v<std::remove_const_t<Callable>, function_ref> && std::is_invocable_r_v<Ret, Callable&, Args...>)
function_ref(Callable& f) noexcept :
callable_(std::bit_cast<void*>(std::addressof(f))),
invoke_(invoke_fn<Callable>)
{}

template<typename Callable>
requires(!std::is_same_v<std::remove_const_t<Callable>, function_ref> && std::is_invocable_r_v<Ret, Callable&, Args...>)
function_ref& operator=(Callable& f) noexcept
{
callable_ = std::bit_cast<void*>(std::addressof(f));
invoke_ = invoke_fn<Callable>;
return *this;
}

Ret operator()(Args... args) { return invoke_(callable_, std::forward<Args>(args)...); }

explicit operator bool() const noexcept { return bool(callable_); }

private:
using InvokeFn = Ret(void*, Args...);

template<typename Callable>
static Ret invoke_fn(void* f, Args... args)
{
return std::invoke(*std::bit_cast<Callable*>(f), std::forward<Args>(args)...);
}

void* callable_ = nullptr;
InvokeFn* invoke_ = nullptr;
};


template<typename...>
class move_only_function;

template<typename R, typename... Args>
class move_only_function<R(Args...)>
{
public:
constexpr move_only_function() noexcept :
fptr_(nullptr)
{}

template<typename F>
requires(!std::is_same_v<F, move_only_function> && std::is_invocable_r_v<R, F&, Args...>)
move_only_function(F f) :
fptr_(std::make_unique<Impl<F, R, Args...>>(std::move(f)))
{}

template<typename F>
requires(!std::is_same_v<F, move_only_function> && std::is_invocable_r_v<R, F&, Args...>)
move_only_function& operator=(F f)
{
fptr_ = std::make_unique<Impl<F, R, Args...>>(std::move(f));
return *this;
}

move_only_function(move_only_function&&) = default;
move_only_function& operator=(move_only_function&& other) = default;

R operator()(Args... args)
{
return fptr_->invoke(std::forward<Args>(args)...);
}

void swap(move_only_function& other) noexcept
{
fptr_.swap(other.fptr_);
}

explicit operator bool() const noexcept { return bool(fptr_); }

private:
template<typename Ret, typename... IArgs>
struct ImplBase
{
virtual Ret invoke(IArgs...) = 0;
virtual ~ImplBase() = default;
};

template<typename Callable, typename Ret, typename... IArgs>
struct Impl : public ImplBase<Ret, IArgs...>
{
constexpr explicit Impl(Callable func) :
func_(std::move(func))
{}

Ret invoke(Args... args) override
{
return std::invoke(func_, std::forward<Args>(args)...);
}

Callable func_;
};

std::unique_ptr<ImplBase<R, Args...>> fptr_;
};

} // namespace gapp::detail

Expand Down
1 change: 0 additions & 1 deletion src/utility/indestructible.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ namespace gapp::detail

~Indestructible() = default;

// These can't be constexpr because bit_cast between ptr types isn't constexpr
T& get() noexcept { return *std::bit_cast<T*>(std::addressof(data_)); }
const T& get() const noexcept { return *std::bit_cast<const T*>(std::addressof(data_)); }

Expand Down
14 changes: 14 additions & 0 deletions src/utility/iterators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@

namespace gapp::detail
{
/* Increment an iterator by n or until it reaches the end of the range. */
template<typename Iter, typename Distance>
constexpr void advance_in_range(Iter& it, Iter last, Distance n)
{
if constexpr (std::random_access_iterator<Iter>)
{
it = (last - n <= it) ? last : it + n;
}
else
{
while (n-- && it != last) ++it;
}
}

/*
* The following should be implemented in Derived:
* begin(), end() with const overloads
Expand Down
54 changes: 0 additions & 54 deletions src/utility/parallel_for.hpp

This file was deleted.

2 changes: 1 addition & 1 deletion src/utility/rcu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
#ifndef GA_UTILITY_RCU_HPP
#define GA_UTILITY_RCU_HPP

#include "utility.hpp"
#include "shared_spinlock.hpp"
#include "indestructible.hpp"
#include "utility.hpp"
#include <atomic>
#include <memory>
#include <mutex>
Expand Down
Loading

0 comments on commit 8f8267d

Please sign in to comment.