Commit 625836aa by FritzFlorian

Sketch minimal serial calling sequence.

The current state shows the minimum actions taken to execute a parallel call: get the thread local, find the active frame, execute on the next frame and return to the active frame.
parent 83c6e622
Pipeline #1385 failed with stages
in 27 seconds
...@@ -17,15 +17,20 @@ int pls_fib(int n) { ...@@ -17,15 +17,20 @@ int pls_fib(int n) {
return 1; return 1;
} }
int a = pls_fib(n - 1); int a, b;
int b = pls_fib(n - 2); scheduler::spawn([n, &a]() {
a = pls_fib(n - 1);
});
scheduler::spawn([n, &b]() {
b = pls_fib(n - 2);
});
return a + b; return a + b;
} }
constexpr int MAX_NUM_THREADS = 1; constexpr int MAX_NUM_THREADS = 1;
constexpr int MAX_NUM_TASKS = 64; constexpr int MAX_NUM_TASKS = 64;
constexpr int MAX_STACK_SIZE = 128; constexpr int MAX_STACK_SIZE = 256;
int main(int argc, char **argv) { int main(int argc, char **argv) {
int num_threads; int num_threads;
......
#include <utility> #include <utility>
#include <cstdio> #include <cstdio>
#include <chrono>
#include "context_switcher/context_switcher.h" #include "context_switcher/context_switcher.h"
using namespace context_switcher;
using namespace std;
const size_t NUM_RUNS = 1000;
// Memory for custom stack and continuation semantics // Memory for custom stack and continuation semantics
const size_t STACK_SIZE = 512 * 8; const size_t STACK_SIZE = 512 * 1;
char custom_stack_1[STACK_SIZE]; const size_t NUM_STACKS = 64;
char custom_stacks[NUM_STACKS][STACK_SIZE];
// Force disable optimization int fib(int n) {
volatile int value = 0; if (n <= 1) {
return 1;
}
int main() { int a, b;
using namespace context_switcher; enter_context(custom_stacks[n], STACK_SIZE, [n, &a](continuation &&cont) {
a = fib(n - 1);
printf("Main 1!\n"); return std::move(cont);
auto cont_2 = enter_context(custom_stack_1, STACK_SIZE, [](continuation &&cont_main) { });
printf("Stack 1!\n"); enter_context(custom_stacks[n], STACK_SIZE, [n, &b](continuation &&cont) {
cont_main = switch_context(std::move(cont_main)); b = fib(n - 2);
printf("Stack 2!\n"); return std::move(cont);
return std::move(cont_main);
}); });
printf("Main 2!\n");
cont_2 = switch_context(std::move(cont_2)); return a + b;
printf("Main 3!\n"); }
volatile int result;
int main() {
auto start_time = chrono::steady_clock::now();
for (unsigned int i = 0; i < NUM_RUNS; i++) {
result = fib(18);
}
auto end_time = chrono::steady_clock::now();
auto time = chrono::duration_cast<chrono::microseconds>(end_time - start_time).count();
printf("%f", (float) time / NUM_RUNS);
return 0; return 0;
} }
...@@ -19,8 +19,8 @@ namespace context_switcher { ...@@ -19,8 +19,8 @@ namespace context_switcher {
template<typename F> template<typename F>
struct lambda_capture { struct lambda_capture {
// TODO: Check if we need an extra template here to perform the move template<typename FARG>
explicit lambda_capture(F &&lambda) : lambda_{std::forward<F>(lambda)} {} explicit lambda_capture(FARG &&lambda) : lambda_{std::forward<FARG>(lambda)} {}
assembly_bindings::continuation_t operator()(assembly_bindings::continuation_t continuation_pointer) { assembly_bindings::continuation_t operator()(assembly_bindings::continuation_t continuation_pointer) {
continuation cont = lambda_(continuation{continuation_pointer}); continuation cont = lambda_(continuation{continuation_pointer});
...@@ -44,8 +44,8 @@ assembly_bindings::continuation_t lambda_capture_callback(assembly_bindings::con ...@@ -44,8 +44,8 @@ assembly_bindings::continuation_t lambda_capture_callback(assembly_bindings::con
} }
template<typename F> template<typename F>
static lambda_capture<F> *place_lambda_capture(F &&lambda, char *memory) { static lambda_capture<typename std::remove_reference<F>::type> *place_lambda_capture(F &&lambda, char *memory) {
return new(memory) lambda_capture<F>(std::forward<F>(lambda)); return new(memory) lambda_capture<typename std::remove_reference<F>::type>(std::forward<F>(lambda));
} }
} }
......
...@@ -54,12 +54,8 @@ class scheduler { ...@@ -54,12 +54,8 @@ class scheduler {
void perform_work(Function work_section); void perform_work(Function work_section);
template<typename Function> template<typename Function>
void spawn(Function &&lambda) { static void spawn(Function &&lambda) {
// TODO: place function on next active thread_state::get().get_task_manager().spawn_child(std::forward<Function>(lambda));
// TODO: capture continuation in current active
// TODO: advance current active
// TODO: after finish, return to last active (if not stolen)
// TODO: revert current active
} }
/** /**
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define PLS_TASK_H #define PLS_TASK_H
#include <utility> #include <utility>
#include <atomic>
#include "context_switcher/continuation.h" #include "context_switcher/continuation.h"
#include "context_switcher/context_switcher.h" #include "context_switcher/context_switcher.h"
...@@ -61,11 +62,22 @@ struct alignas(base::system_details::CACHE_LINE_SIZE) task { ...@@ -61,11 +62,22 @@ struct alignas(base::system_details::CACHE_LINE_SIZE) task {
parent_task_ = parent_task; parent_task_ = parent_task;
} }
context_switcher::continuation get_continuation() {
return std::move(continuation_);
}
void set_continuation(context_switcher::continuation &&continuation) {
continuation_ = std::move(continuation);
}
template<typename F> template<typename F>
context_switcher::continuation run_as_task(F &&lambda) { context_switcher::continuation run_as_task(F &&lambda) {
return context_switcher::enter_context(stack_memory_, stack_size_, std::forward<F>(lambda)); return context_switcher::enter_context(stack_memory_, stack_size_, std::forward<F>(lambda));
} }
// TODO: Remove and add proper version
// Simulate 'fast' syncronization
std::atomic<int> flag_{0};
private: private:
// Stack/Continuation Management // Stack/Continuation Management
char *stack_memory_; char *stack_memory_;
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include <utility> #include <utility>
#include <array> #include <array>
#include "context_switcher/continuation.h"
#include "pls/internal/scheduling/task.h" #include "pls/internal/scheduling/task.h"
#include "pls/internal/data_structures/bounded_trading_deque.h" #include "pls/internal/data_structures/bounded_trading_deque.h"
...@@ -54,6 +56,31 @@ class task_manager { ...@@ -54,6 +56,31 @@ class task_manager {
return *active_task_; return *active_task_;
} }
template<typename F>
void spawn_child(F &&lambda) {
// TODO: Here is some potential for optimization. We could try placing everything manually on the stack.
active_task_->get_next()->run_as_task([lambda, this](context_switcher::continuation cont) {
auto *last_task = active_task_;
auto *this_task = active_task_->get_next();
last_task->set_continuation(std::move(cont));
active_task_ = this_task;
// TODO: Publish last task on deque (do this properly, but this simulates the fastest possible impl)
// last_task->flag_.store(1, std::memory_order_seq_cst);
lambda();
// TODO: Check if task was stolen from deque (do this properly, but this simulates the fastest possible impl)
// if (last_task->flag_.exchange(0, std::memory_order_seq_cst) == 1) {
active_task_ = last_task;
return std::move(last_task->get_continuation());
// } else {
// return context_switcher::continuation{nullptr};
// }
});
}
private: private:
size_t num_tasks_; size_t num_tasks_;
......
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