From 3a6b724dc722ba158bbc6fdf7c96018ba5df6fce Mon Sep 17 00:00:00 2001 From: FritzFlorian Date: Mon, 20 Jan 2020 21:28:56 +0100 Subject: [PATCH] Add alternative context switch implementation (similar to boost). --- CMakeLists.txt | 1 + app/context_switch/CMakeLists.txt | 4 ++-- app/context_switch/fiber_call_x86_64.s | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/context_switch/fiber_continue_x86_64.s | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/context_switch/main.cpp | 17 ++++++++++++++++- app/playground/main.cpp | 4 ++++ 6 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 app/context_switch/fiber_call_x86_64.s create mode 100644 app/context_switch/fiber_continue_x86_64.s diff --git a/CMakeLists.txt b/CMakeLists.txt index d5bcb81..472df57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ add_subdirectory(app/benchmark_matrix) add_subdirectory(app/benchmark_prefix) add_subdirectory(app/benchmark_pipeline) add_subdirectory(app/benchmark_fib) +add_subdirectory(app/context_switch) # Add optional tests option(PACKAGE_TESTS "Build the tests" ON) diff --git a/app/context_switch/CMakeLists.txt b/app/context_switch/CMakeLists.txt index 2f4849d..7a3f40b 100644 --- a/app/context_switch/CMakeLists.txt +++ b/app/context_switch/CMakeLists.txt @@ -7,7 +7,7 @@ else () endif () -add_executable(playground main.cpp ${SWITCH_ASSEMBLY}) +add_executable(context_switch main.cpp ${SWITCH_ASSEMBLY} fiber_call_x86_64.s fiber_continue_x86_64.s) # Example for adding the library to your app (as a cmake project dependency) -target_link_libraries(playground) +target_link_libraries(context_switch) diff --git a/app/context_switch/fiber_call_x86_64.s b/app/context_switch/fiber_call_x86_64.s new file mode 100644 index 0000000..d9605f6 --- /dev/null +++ b/app/context_switch/fiber_call_x86_64.s @@ -0,0 +1,55 @@ + .file "fiber_call_x86_64.s" + .text + .global fiber_call + .type fiber_call, @function + +.align 16 +fiber_call: + # rdi = address of new stack top and to store current stack top (parameter to function) + # r12 temporary for restoring old stack (callee saved, so we can take a 'fast' return path) + + ############### Register State Storage ################ + # make space for all register state we will store + leaq -0x38(%rsp), %rsp + + # store calee saved general registers + movq %r12, 0x00(%rsp) + movq %r13, 0x08(%rsp) + movq %r14, 0x10(%rsp) + movq %r15, 0x18(%rsp) + movq %rbx, 0x20(%rsp) + movq %rbp, 0x28(%rsp) + # store MMX control- and status-word + stmxcsr 0x30(%rsp) + # store x87 control-word + fnstcw 0x34(%rsp) + + ############### Stack Pointer Manipulation ############# + # keep the old stack pointer in a callee-saved register for 'fast' return path + movq %rsp, %r12 + # switch to new stack pointer + movq (%rdi), %rsp + # store old stack pointer + movq %r12, (%rdi) + + ############# Function Call ############################ + # perform actual function call, this will now be on the new stack + call callback + # If someone else continued/jumped into this stored context we must not return here, + # thus we will never execute the rest of the function. + # Otherwise we can return out of the function as if it was a simple function call. + + ############ Stack Pointer Manipulation ################ + # restore stack pointer that we had before + movq %r12, %rsp + + ############ Restore State Storage (as needed) ######### + # ...we don't need to restore most registers (as they are calee saved). + # We only used %r12, so restore that and move back the stack pointer. + movq 0x00(%rsp), %r12 + + # release the space we used for storage + leaq 0x38(%rsp), %rsp + + # just return back from the call + ret diff --git a/app/context_switch/fiber_continue_x86_64.s b/app/context_switch/fiber_continue_x86_64.s new file mode 100644 index 0000000..4c3e4e6 --- /dev/null +++ b/app/context_switch/fiber_continue_x86_64.s @@ -0,0 +1,54 @@ + .file "fiber_continue_x86_64.s" + .text + .global fiber_continue + .type fiber_continue, @function + +.align 16 +fiber_continue: + # rdi = address of new stack top and to store current stack top (parameter to function) + + ############### Register State Storage ################ + # make space for all register state we will store + leaq -0x38(%rsp), %rsp + + # store calee saved general registers + movq %r12, 0x00(%rsp) + movq %r13, 0x08(%rsp) + movq %r14, 0x10(%rsp) + movq %r15, 0x18(%rsp) + movq %rbx, 0x20(%rsp) + movq %rbp, 0x28(%rsp) + # store MMX control- and status-word + stmxcsr 0x30(%rsp) + # store x87 control-word + fnstcw 0x34(%rsp) + + ############### Stack Pointer Manipulation ############# + # keep the old stack + movq %rsp, %r12 + # switch to new stack pointer + movq (%rdi), %rsp + # store old stack pointer + movq %r12, (%rdi) + + # We stored our old execution context and are now on the target stack + # on which we would like to contiune our execution. + + ############ Restore State Storage (as needed) ######### + # restore calee saved general registers + movq 0x00(%rsp), %r12 + movq 0x08(%rsp), %r13 + movq 0x10(%rsp), %r14 + movq 0x18(%rsp), %r15 + movq 0x20(%rsp), %rbx + movq 0x28(%rsp), %rbp + # restore MMX control- and status-word + ldmxcsr 0x30(%rsp) + # restore x87 control-word + fldcw 0x34(%rsp) + + # Free space for restored state + leaq 0x38(%rsp), %rsp + + # just return back from the call + ret diff --git a/app/context_switch/main.cpp b/app/context_switch/main.cpp index 5fb3c80..7d285b6 100644 --- a/app/context_switch/main.cpp +++ b/app/context_switch/main.cpp @@ -7,7 +7,7 @@ using namespace std; // Settings for stack and benchmark const unsigned int NUM_RUNS = 100000; -const unsigned int STACK_SIZE = 512 * 1; +const unsigned int STACK_SIZE = 512 * 8; const unsigned char MAGIC_NUMBER = (unsigned char) 0xAB; // Memory for custom stack and continuation semantics @@ -18,6 +18,9 @@ jmp_buf buffer; extern "C" { void custom_stack_callback(void *); +void fiber_call(void *); +void fiber_continue(void *); + void __attribute__ ((noinline)) callback() { static volatile int tmp; tmp = 0; // Force at least a single memory write @@ -65,6 +68,16 @@ long __attribute__ ((noinline)) measure_continuation_and_jump() { return chrono::duration_cast(end_time - start_time).count(); } +long __attribute__ ((noinline)) measure_fiber_call() { + auto start_time = chrono::steady_clock::now(); + for (unsigned int i = 0; i < NUM_RUNS; i++) { + void *stack_pointer = custom_stack; + fiber_call(&stack_pointer); + } + auto end_time = chrono::steady_clock::now(); + return chrono::duration_cast(end_time - start_time).count(); +} + int main() { memset(custom_stack, MAGIC_NUMBER, STACK_SIZE); @@ -72,6 +85,7 @@ int main() { auto time_cont = measure_continuation(); auto time_stack = measure_stack_switch(); auto time_func = measure_function_call(); + auto time_fiber = measure_fiber_call(); for (unsigned int i = 0; i < STACK_SIZE; i++) { if (custom_stack[i] != MAGIC_NUMBER) { @@ -84,6 +98,7 @@ int main() { printf("Stack Switching : %10ld, %5.5f\n", time_stack, ((float) time_stack / NUM_RUNS)); printf("Full Continuation: %10ld, %5.5f\n", time_cont, ((float) time_cont / NUM_RUNS)); printf("Jump Continuation: %10ld, %5.5f\n", time_cont_jump, ((float) time_cont_jump / NUM_RUNS)); + printf("Fiber Call : %10ld, %5.5f\n", time_fiber, ((float) time_fiber / NUM_RUNS)); return 0; } diff --git a/app/playground/main.cpp b/app/playground/main.cpp index 18d3906..a1c17b1 100644 --- a/app/playground/main.cpp +++ b/app/playground/main.cpp @@ -1,5 +1,9 @@ +#include +#include int main() { + printf("Buffer Size: %u\n", sizeof(jmp_buf)); + printf("Big Buffer Size: %u\n", sizeof(sigjmp_buf)); return 0; } -- libgit2 0.26.0