Commit a5bb074c by FritzFlorian

Refactor: Use perfect forwarding instead of copy constructor.

parent 0228aa92
Pipeline #1260 failed with stages
in 43 seconds
...@@ -34,8 +34,10 @@ add_library(pls STATIC ...@@ -34,8 +34,10 @@ add_library(pls STATIC
include/pls/internal/scheduling/scheduler_impl.h include/pls/internal/scheduling/scheduler_impl.h
include/pls/internal/scheduling/task.h src/internal/scheduling/task.cpp include/pls/internal/scheduling/task.h src/internal/scheduling/task.cpp
include/pls/internal/scheduling/scheduler_memory.h src/internal/scheduling/scheduler_memory.cpp include/pls/internal/scheduling/scheduler_memory.h src/internal/scheduling/scheduler_memory.cpp
include/pls/internal/scheduling/lambda_task.h include/pls/internal/data_structures/deque.h) include/pls/internal/scheduling/lambda_task.h include/pls/internal/data_structures/deque.h
# include/pls/algorithms/parallel_scan.h include/pls/algorithms/parallel_scan_impl.h)
)
# Add everything in `./include` to be in the include path of this project # Add everything in `./include` to be in the include path of this project
target_include_directories(pls target_include_directories(pls
PUBLIC PUBLIC
......
...@@ -11,27 +11,27 @@ namespace pls { ...@@ -11,27 +11,27 @@ namespace pls {
namespace algorithm { namespace algorithm {
template<typename Function1, typename Function2> template<typename Function1, typename Function2>
void invoke_parallel(const Function1 &function1, const Function2 &function2) { void invoke_parallel(Function1 &&function1, Function2 &&function2) {
using namespace ::pls::internal::scheduling; using namespace ::pls::internal::scheduling;
auto sub_task_1 = lambda_task_by_reference<Function1>(function1); using task_1_t = lambda_task_by_value<Function1>;
auto sub_task_2 = lambda_task_by_reference<Function2>(function2); using task_2_t = lambda_task_by_value<Function2>;
scheduler::spawn_child(sub_task_2); scheduler::spawn_child<task_2_t>(std::forward<Function2>(function2));
scheduler::spawn_child_and_wait(sub_task_1); scheduler::spawn_child_and_wait<task_1_t>(std::forward<Function1>(function1));
} }
template<typename Function1, typename Function2, typename Function3> template<typename Function1, typename Function2, typename Function3>
void invoke_parallel(const Function1 &function1, const Function2 &function2, const Function3 &function3) { void invoke_parallel(Function1 &&function1, Function2 &&function2, Function3 &&function3) {
using namespace ::pls::internal::scheduling; using namespace ::pls::internal::scheduling;
auto sub_task_1 = lambda_task_by_reference<Function1>(function1); using task_1_t = lambda_task_by_value<Function1>;
auto sub_task_2 = lambda_task_by_reference<Function2>(function2); using task_2_t = lambda_task_by_value<Function2>;
auto sub_task_3 = lambda_task_by_reference<Function3>(function3); using task_3_t = lambda_task_by_value<Function3>;
scheduler::spawn_child(sub_task_3); scheduler::spawn_child<task_3_t>(std::forward<Function3>(function3));
scheduler::spawn_child(sub_task_2); scheduler::spawn_child<task_2_t>(std::forward<Function2>(function2));
scheduler::spawn_child_and_wait(sub_task_1); scheduler::spawn_child_and_wait<task_1_t>(std::forward<Function1>(function1));
} }
} }
......
...@@ -25,14 +25,13 @@ void parallel_for(RandomIt first, RandomIt last, const Function &function) { ...@@ -25,14 +25,13 @@ void parallel_for(RandomIt first, RandomIt last, const Function &function) {
// Cut in half recursively // Cut in half recursively
long middle_index = num_elements / 2; long middle_index = num_elements / 2;
auto body2 = [=] { parallel_for(first + middle_index, last, function); }; auto second_half_body = [=] { parallel_for(first + middle_index, last, function); };
lambda_task_by_reference<decltype(body2)> second_half_task(body2); using second_half_t = lambda_task_by_reference<decltype(second_half_body)>;
scheduler::spawn_child(second_half_task); scheduler::spawn_child<second_half_t>(std::move(second_half_body));
auto body1 = [=] { parallel_for(first, first + middle_index, function); }; auto first_half_body = [=] { parallel_for(first, first + middle_index, function); };
lambda_task_by_reference<decltype(body1)> first_half_task(body1); using first_half_t = lambda_task_by_reference<decltype(first_half_body)>;
scheduler::spawn_child(first_half_task); scheduler::spawn_child_and_wait<first_half_t>(std::move(first_half_body));
scheduler::wait_for_all();
} }
} }
......
...@@ -40,8 +40,8 @@ class aligned_stack { ...@@ -40,8 +40,8 @@ class aligned_stack {
aligned_stack(pointer_t memory_region, std::size_t size); aligned_stack(pointer_t memory_region, std::size_t size);
aligned_stack(char *memory_region, std::size_t size); aligned_stack(char *memory_region, std::size_t size);
template<typename T> template<typename T, typename ...ARGS>
T *push(const T &object); T *push(ARGS &&... args);
template<typename T> template<typename T>
void *push(); void *push();
template<typename T> template<typename T>
......
...@@ -6,10 +6,10 @@ namespace pls { ...@@ -6,10 +6,10 @@ namespace pls {
namespace internal { namespace internal {
namespace data_structures { namespace data_structures {
template<typename T> template<typename T, typename ...ARGS>
T *aligned_stack::push(const T &object) { T *aligned_stack::push(ARGS &&... args) {
// Copy-Construct // Perfect-Forward construct
return new(push < T > ())T(object); return new(push < T > ())T(std::forward<ARGS>(args)...);
} }
template<typename T> template<typename T>
......
...@@ -81,8 +81,8 @@ class work_stealing_deque { ...@@ -81,8 +81,8 @@ class work_stealing_deque {
tail_{other.tail_.load()}, tail_{other.tail_.load()},
previous_tail_{other.previous_tail_} {} previous_tail_{other.previous_tail_} {}
template<typename T> template<typename T, typename Function, typename ...ARGS>
T *push_tail(const T &new_item); T *push_tail(const Function &after_creation, ARGS &&... args);
Item *pop_tail(); Item *pop_tail();
Item *pop_head(); Item *pop_head();
...@@ -94,8 +94,8 @@ class work_stealing_deque { ...@@ -94,8 +94,8 @@ class work_stealing_deque {
work_stealing_deque_item *item_at(offset_t offset); work_stealing_deque_item *item_at(offset_t offset);
offset_t current_stack_offset(); offset_t current_stack_offset();
template<typename T> template<typename T, typename ...ARGS>
std::pair<work_stealing_deque_item, T> *allocate_item(const T &new_item); std::pair<work_stealing_deque_item, T> *allocate_item(ARGS &&... args);
}; };
} }
......
...@@ -23,28 +23,30 @@ offset_t work_stealing_deque<Item>::current_stack_offset() { ...@@ -23,28 +23,30 @@ offset_t work_stealing_deque<Item>::current_stack_offset() {
} }
template<typename Item> template<typename Item>
template<typename T> template<typename T, typename ...ARGS>
std::pair<work_stealing_deque_item, T> *work_stealing_deque<Item>::allocate_item(const T &new_item) { std::pair<work_stealing_deque_item, T> *work_stealing_deque<Item>::allocate_item(ARGS &&... args) {
// 'Union' type to push both on stack // 'Union' type to push both on stack
using pair_t = std::pair<work_stealing_deque_item, T>; using pair_t = std::pair<work_stealing_deque_item, T>;
// Allocate space on stack // Allocate space on stack
auto new_pair = reinterpret_cast<pair_t *>(stack_->push<pair_t>()); auto new_pair = reinterpret_cast<pair_t *>(stack_->push<pair_t>());
// Initialize memory on stack // Initialize memory on stack
new((void *) &(new_pair->first)) work_stealing_deque_item(); new((void *) &(new_pair->first)) work_stealing_deque_item();
new((void *) &(new_pair->second)) T(new_item); new((void *) &(new_pair->second)) T(std::forward<ARGS>(args)...);
return new_pair; return new_pair;
} }
template<typename Item> template<typename Item>
template<typename T> template<typename T, typename Function, typename ...ARGS>
T *work_stealing_deque<Item>::push_tail(const T &new_item) { T *work_stealing_deque<Item>::push_tail(const Function &after_creation, ARGS &&... args) {
static_assert(std::is_same<Item, T>::value || std::is_base_of<Item, T>::value, static_assert(std::is_same<Item, T>::value || std::is_base_of<Item, T>::value,
"Must only push types of <Item> onto work_stealing_deque<Item>"); "Must only push types of <Item> onto work_stealing_deque<Item>");
offset_t local_tail = tail_; offset_t local_tail = tail_;
auto new_pair = allocate_item(new_item); auto new_pair = allocate_item<T>(std::forward<ARGS>(args)...);
after_creation(&(new_pair->second)); // callback for time after creation but before being visible to others
// Prepare current tail to point to correct next items // Prepare current tail to point to correct next items
auto tail_deque_item = item_at(local_tail); auto tail_deque_item = item_at(local_tail);
tail_deque_item->set_data(&(new_pair->second)); tail_deque_item->set_data(&(new_pair->second));
......
...@@ -79,19 +79,21 @@ class scheduler { ...@@ -79,19 +79,21 @@ class scheduler {
* Helper to spawn a child on the currently running task. * Helper to spawn a child on the currently running task.
* *
* @tparam T type of the new task * @tparam T type of the new task
* @param sub_task the new task to be spawned * @tparam ARGS Constructor argument types
* @param args constructor arguments
*/ */
template<typename T> template<typename T, typename ...ARGS>
static void spawn_child(T &sub_task); static void spawn_child(ARGS &&... args);
/** /**
* Helper to spawn a child on the currently running task and waiting for it (skipping over the task-deque). * Helper to spawn a child on the currently running task and waiting for it (skipping over the task-deque).
* *
* @tparam T type of the new task * @tparam T type of the new task
* @param sub_task the new task to be spawned * @tparam ARGS Constructor argument types
* @param args constructor arguments
*/ */
template<typename T> template<typename T, typename ...ARGS>
static void spawn_child_and_wait(T &sub_task); static void spawn_child_and_wait(ARGS &&... args);
/** /**
* Helper to wait for all children of the currently executing task. * Helper to wait for all children of the currently executing task.
......
...@@ -40,14 +40,14 @@ void scheduler::perform_work(Function work_section) { ...@@ -40,14 +40,14 @@ void scheduler::perform_work(Function work_section) {
} }
} }
template<typename T> template<typename T, typename ...ARGS>
void scheduler::spawn_child(T &sub_task) { void scheduler::spawn_child(ARGS &&... args) {
thread_state::get()->current_task_->spawn_child(sub_task); thread_state::get()->current_task_->spawn_child<T>(std::forward<ARGS>(args)...);
} }
template<typename T> template<typename T, typename ...ARGS>
void scheduler::spawn_child_and_wait(T &sub_task) { void scheduler::spawn_child_and_wait(ARGS &&... args) {
thread_state::get()->current_task_->spawn_child_and_wait(sub_task); thread_state::get()->current_task_->spawn_child_and_wait<T>(std::forward<ARGS>(args)...);
} }
} }
......
...@@ -24,48 +24,48 @@ class task { ...@@ -24,48 +24,48 @@ class task {
data_structures::deque<task>::state deque_state_; data_structures::deque<task>::state deque_state_;
protected: protected:
// TODO: Double Check with copy and move constructors, try to minimize overhead while keeping a clean API.
explicit task(); explicit task();
task(const task &other);
/** /**
* Overwrite this with the actual behaviour of concrete tasks. * Overwrite this with the actual behaviour of concrete tasks.
*/ */
virtual void execute_internal() = 0; virtual void execute_internal() = 0;
template<typename T> template<typename T, typename ...ARGS>
void spawn_child(T &&sub_task); void spawn_child(ARGS &&... args);
template<typename T> template<typename T, typename ...ARGS>
void spawn_child_and_wait(T &&sub_task); void spawn_child_and_wait(ARGS &&... args);
void wait_for_all(); void wait_for_all();
private: private:
void execute(); void execute();
}; };
template<typename T> template<typename T, typename ...ARGS>
void task::spawn_child(T &&sub_task) { void task::spawn_child(ARGS &&... args) {
PROFILE_FORK_JOIN_STEALING("spawn_child") PROFILE_FORK_JOIN_STEALING("spawn_child")
static_assert(std::is_base_of<task, typename std::remove_reference<T>::type>::value, "Only pass task subclasses!"); static_assert(std::is_base_of<task, typename std::remove_reference<T>::type>::value, "Only pass task subclasses!");
// Keep our refcount up to date // Keep our refcount up to date
ref_count_++; ref_count_++;
// Assign forced values (for stack and parent management)
sub_task.parent_ = this;
sub_task.deque_state_ = thread_state::get()->deque_.save_state();
// Push on our deque // Push on our deque
const T const_task = sub_task; auto deque_state = thread_state::get()->deque_.save_state();
thread_state::get()->deque_.push_tail(const_task); thread_state::get()->deque_.push_tail<T>([this, deque_state](T *item) {
// Assign forced values (for stack and parent management)
item->parent_ = this;
item->deque_state_ = deque_state;
}, std::forward<ARGS>(args)...);
} }
template<typename T> template<typename T, typename ...ARGS>
void task::spawn_child_and_wait(T &&sub_task) { void task::spawn_child_and_wait(ARGS &&... args) {
PROFILE_FORK_JOIN_STEALING("spawn_child_wait") PROFILE_FORK_JOIN_STEALING("spawn_child_wait")
static_assert(std::is_base_of<task, typename std::remove_reference<T>::type>::value, "Only pass task subclasses!"); static_assert(std::is_base_of<task, typename std::remove_reference<T>::type>::value, "Only pass task subclasses!");
// Assign forced values (for stack and parent management) // Assign forced values (for stack and parent management)
// TODO: Move this after construction
T sub_task{std::forward<ARGS>(args)...};
sub_task.parent_ = nullptr; sub_task.parent_ = nullptr;
sub_task.deque_state_ = thread_state::get()->deque_.save_state(); sub_task.deque_state_ = thread_state::get()->deque_.save_state();
PROFILE_END_BLOCK PROFILE_END_BLOCK
......
...@@ -13,11 +13,6 @@ task::task() : ...@@ -13,11 +13,6 @@ task::task() :
parent_{nullptr}, parent_{nullptr},
deque_state_{0} {} deque_state_{0} {}
task::task(const task &other) :
ref_count_{0},
parent_{other.parent_},
deque_state_{other.deque_state_} {}
void task::execute() { void task::execute() {
PROFILE_WORK_BLOCK("execute task") PROFILE_WORK_BLOCK("execute task")
auto last_executing = thread_state::get()->current_task_; auto last_executing = thread_state::get()->current_task_;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment