Commit 39d2fbd8 by FritzFlorian

WIP: Sketch fast path of task manager.

parent d3b64a85
Pipeline #1334 failed with stages
in 25 seconds
...@@ -13,9 +13,9 @@ namespace internal { ...@@ -13,9 +13,9 @@ namespace internal {
namespace base { namespace base {
namespace alignment { namespace alignment {
system_details::pointer_t next_alignment(system_details::pointer_t size); constexpr system_details::pointer_t next_alignment(system_details::pointer_t size);
constexpr system_details::pointer_t previous_alignment(system_details::pointer_t size);
char *next_alignment(char *pointer); char *next_alignment(char *pointer);
system_details::pointer_t previous_alignment(system_details::pointer_t size);
/** /**
* Forces alignment requirements on a type equal to a cache line size. * Forces alignment requirements on a type equal to a cache line size.
......
...@@ -23,7 +23,7 @@ constexpr system_details::pointer_t previous_alignment(system_details::pointer_t ...@@ -23,7 +23,7 @@ constexpr system_details::pointer_t previous_alignment(system_details::pointer_t
size - (size % system_details::CACHE_LINE_SIZE); size - (size % system_details::CACHE_LINE_SIZE);
} }
constexpr char *next_alignment(char *pointer) { char *next_alignment(char *pointer) {
return reinterpret_cast<char *>(next_alignment(reinterpret_cast<system_details::pointer_t >(pointer))); return reinterpret_cast<char *>(next_alignment(reinterpret_cast<system_details::pointer_t >(pointer)));
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
#define PLS_CONT_MANAGER_H_ #define PLS_CONT_MANAGER_H_
#include <memory> #include <memory>
#include <tuple> #include <utility>
#include <array> #include <array>
#include "pls/internal/data_structures/aligned_stack.h" #include "pls/internal/data_structures/aligned_stack.h"
...@@ -42,11 +42,11 @@ class cont_manager { ...@@ -42,11 +42,11 @@ class cont_manager {
continuation_node *cont_chain_start, continuation_node *cont_chain_start,
continuation_node *prev) { continuation_node *prev) {
// Represents one cont node and its corresponding memory buffer (as one continuous block of memory). // Represents one cont node and its corresponding memory buffer (as one continuous block of memory).
using cont_node_memory_pair = std::tuple<continuation_node, using cont_node_memory_pair = std::pair<continuation_node,
std::array<char, MAX_CONT_SIZE - sizeof(continuation_node)>>; std::array<char, MAX_CONT_SIZE - sizeof(continuation_node)>>;
char *tuple_memory = cont_storage.push_bytes<cont_node_memory_pair>(); char *pair_memory = cont_storage.push_bytes<cont_node_memory_pair>();
char *cont_node_address = tuple_memory; char *cont_node_address = pair_memory;
char *cont_node_memory_address = tuple_memory + sizeof(continuation_node); char *cont_node_memory_address = pair_memory + sizeof(continuation_node);
return new(cont_node_address) continuation_node(cont_node_memory_address, cont_chain_start, prev); return new(cont_node_address) continuation_node(cont_node_memory_address, cont_chain_start, prev);
} }
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
#ifndef PLS_TASK_H #ifndef PLS_TASK_H
#define PLS_TASK_H #define PLS_TASK_H
#include "pls/internal/scheduling/task_manager.h"
#include "pls/internal/scheduling/thread_state.h" #include "pls/internal/scheduling/thread_state.h"
#include "pls/internal/scheduling/continuation.h"
namespace pls { namespace pls {
namespace internal { namespace internal {
...@@ -15,26 +15,11 @@ namespace scheduling { ...@@ -15,26 +15,11 @@ namespace scheduling {
* *
* Override the execute_internal() method for your custom code. * Override the execute_internal() method for your custom code.
*/ */
class task { class base_task {
friend class scheduler; friend class scheduler;
// TODO: Add ref to continuation
task_manager::task_manager_state task_manager_state_;
protected: protected:
explicit task(); base_task() = default;
/**
* Allow to allocate extra memory during run-time for this task.
* Memory will be pushed onto the stack (in aligned memory, thus avoid many small chunks).
*
* Memory is fully self managed. Calling e.g. de-constructors when not needing objects
* anymore is the users responsibility (memory is simply re-used after the life time of the task ends).
*
* @param size Number of bytes to be allocated
* @return The allocated memory region
*/
void *allocate_memory(long size);
/** /**
* Overwrite this with the actual behaviour of concrete tasks. * Overwrite this with the actual behaviour of concrete tasks.
...@@ -42,7 +27,27 @@ class task { ...@@ -42,7 +27,27 @@ class task {
virtual void execute_internal() = 0; virtual void execute_internal() = 0;
private: private:
void execute(); void execute() {
// TODO: Figure out slow path execution
execute_internal();
}
};
template<typename F, typename R1, typename R2, typename CF>
class task : public base_task {
public:
template<typename FARG>
explicit task(FARG &&function, continuation<R1, R2, CF> *continuation)
: base_task{}, function_{std::forward<FARG>(function)}, continuation_{continuation} {}
void execute_internal() override {
continuation_->store_result_2(function_());
// TODO: Properly notify continuation on slow path
}
private:
F function_;
continuation<R1, R2, CF> *continuation_;
}; };
} }
......
...@@ -3,15 +3,26 @@ ...@@ -3,15 +3,26 @@
#define PLS_TASK_MANAGER_H_ #define PLS_TASK_MANAGER_H_
#include <memory> #include <memory>
#include <utility>
#include <array>
#include <atomic>
#include "pls/internal/data_structures/aligned_stack.h" #include "pls/internal/scheduling/task.h"
#include "pls/internal/data_structures/stamped_integer.h"
#include "task.h"
namespace pls { namespace pls {
namespace internal { namespace internal {
namespace scheduling { namespace scheduling {
// TODO: Remove forward references struct task_handle {
class task; public:
enum state { uninitialized, initialized, execute_local, stealing, execute_remote, finished };
using stamped_state = data_structures::stamped_integer;
std::atomic<stamped_state> stamped_state_{uninitialized};
base_task *task_;
};
/** /**
* Handles management of tasks in the system. Each thread has a local task manager, * Handles management of tasks in the system. Each thread has a local task manager,
...@@ -22,42 +33,65 @@ class task; ...@@ -22,42 +33,65 @@ class task;
* integrate the memory management into the stealing procedure. * integrate the memory management into the stealing procedure.
*/ */
class task_manager { class task_manager {
using task_manager_offset = data_structures::aligned_stack::stack_offset;
public: public:
// Data each task needs to store to enable the 'return_task' functionality.
using task_manager_state = task_manager_offset;
// Construct a task onto the stack. Stores the previous offset in the newly constructed task.
template<class T, typename ...ARGS>
T *push_task(ARGS ...args);
// Publishes a task on the stack, i.e. makes it visible for other threads to steal. // Publishes a task on the stack, i.e. makes it visible for other threads to steal.
void publish_task(task *task); // The task itself is located on the stack of the worker, as the stealer will copy it away before it is freed.
// Return a no longer needed task to the stack. Must be the current most top task (will reset the stack pointer). void publish_task(base_task &task) {
void return_task(task *task); task_handle_stack_[tail_internal_].task_ = &task;
task_handle_stack_[tail_internal_].stamped_state_.store({stamp_internal_++, task_handle::initialized},
std::memory_order_relaxed);
tail_internal_++;
tail_.store(tail_internal_, std::memory_order_release); // Linearization point, handle is published here
}
// Try to pop a local task from this task managers stack. // Try to pop a local task from this task managers stack.
task *pop_local_task(); // This should only be required on the fast path of the implementation,
// Try to steal a task from a remote task_manager instance. // thus only returning if the operation was a success.
// The returned task pointer is valid during the lifetyme of the task. // Essentially this is an 'un-publish' of a task with a notion if it was successful.
// The returned task pointer must be returned to this task_manager instance. bool steal_local_task() {
// (This is because we can either decide to just steal a remote task pointer or to copy the whole task) tail_internal_--;
task *pop_remote_task(task_manager &other); tail_.store(tail_internal_, std::memory_order_relaxed);
task_handle::stamped_state swapped_state{task_handle::execute_local, stamp_internal_++};
task_handle_stack_[tail_internal_].stamped_state_.exchange(swapped_state, std::memory_order_acq_rel);
if (swapped_state.value == task_handle::execute_remote ||
swapped_state.value == task_handle::finished) {
// Someone got the other task, return to 'non linear' execution path
// TODO: Properly handle slow path
return false;
} else {
// No one got the task so far, we are happy and continue our fast path
return true;
}
}
// Try to steal a task from a remote task_manager instance. The stolen task must be stored locally.
// Returns a pair containing the actual task and if the steal was successful.
// TODO: Re-implement after fast path is done
// std::pair<task, bool> steal_remote_task(task_manager &other);
explicit task_manager(data_structures::aligned_stack &task_stack) : task_stack_{task_stack} {} explicit task_manager(task_handle *task_handle_stack) : task_handle_stack_{task_handle_stack},
head_{{0}},
tail_{0},
tail_internal_{0},
stamp_internal_{0} {}
private: private:
data_structures::aligned_stack &task_stack_; task_handle *task_handle_stack_;
alignas(base::system_details::CACHE_LINE_SIZE) std::atomic<std::atomic<data_structures::stamped_integer>> head_;
alignas(base::system_details::CACHE_LINE_SIZE) std::atomic<unsigned int> tail_;
alignas(base::system_details::CACHE_LINE_SIZE) unsigned int tail_internal_, stamp_internal_;
}; };
template<size_t NUM_TASKS, size_t MAX_STACK_SIZE> template<size_t NUM_TASKS, size_t MAX_STACK_SIZE>
class static_task_manager { class static_task_manager {
public: public:
static_task_manager() : static_task_stack_{}, task_manager_{static_task_stack_} {}; static_task_manager() : static_task_handle_stack_{}, task_manager_{static_task_handle_stack_.data()} {};
task_manager &get_task_manager() { return task_manager_; } task_manager &get_task_manager() { return task_manager_; }
private: private:
data_structures::static_aligned_stack<MAX_STACK_SIZE> static_task_stack_; std::array<task_handle, NUM_TASKS> static_task_handle_stack_;
task_manager task_manager_; task_manager task_manager_;
}; };
......
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