diff --git a/app/context_switch/CMakeLists.txt b/app/context_switch/CMakeLists.txt new file mode 100644 index 0000000..2f4849d --- /dev/null +++ b/app/context_switch/CMakeLists.txt @@ -0,0 +1,13 @@ +if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND CMAKE_SYSTEM_NAME STREQUAL "Linux") + SET(SWITCH_ASSEMBLY "custom_stack_callback_x86_64.s") +elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm" AND CMAKE_SYSTEM_NAME STREQUAL "Linux") + SET(SWITCH_ASSEMBLY "custom_stack_callback_arm32.s") +else () + MESSAGE(FATAL_ERROR "Platform (${CMAKE_SYSTEM_PROCESSOR} on ${CMAKE_SYSTEM_NAME}) not supported! Please see Readme for instructions to port.") +endif () + + +add_executable(playground main.cpp ${SWITCH_ASSEMBLY}) + +# Example for adding the library to your app (as a cmake project dependency) +target_link_libraries(playground) diff --git a/app/context_switch/custom_stack_callback_arm32.s b/app/context_switch/custom_stack_callback_arm32.s new file mode 100644 index 0000000..dd81eeb --- /dev/null +++ b/app/context_switch/custom_stack_callback_arm32.s @@ -0,0 +1,15 @@ + .arm + .text + .global custom_stack_callback + .type custom_stack_callback, %function + +.align 4 +custom_stack_callback: + /* r0 new stack adress (passed as parameter) */ + /* r4 temporary for restoring old stack (callee saved, so we get the correct value in case of a return) */ + push {r4, lr} /* store the callee saved register as required and the return address */ + mov r4, sp /* store current stack pointer */ + mov sp, r0 /* update stack pointer to new user level stack */ + bl callback /* enter next tasks (will not return if continuation is stolen) */ + mov sp, r4 /* restore to the old stack pointer */ + pop {r4, pc} /* restore the callee saved register as required and returns */ diff --git a/app/playground/custom_stack_callback.s b/app/context_switch/custom_stack_callback_x86_64.s similarity index 94% rename from app/playground/custom_stack_callback.s rename to app/context_switch/custom_stack_callback_x86_64.s index 085350c..5a6d71e 100644 --- a/app/playground/custom_stack_callback.s +++ b/app/context_switch/custom_stack_callback_x86_64.s @@ -1,4 +1,4 @@ - .file "custom_stack_callback.s" + .file "custom_stack_callback_x86_64.s" .text .global custom_stack_callback .type custom_stack_callback, @function diff --git a/app/context_switch/main.cpp b/app/context_switch/main.cpp new file mode 100644 index 0000000..5fb3c80 --- /dev/null +++ b/app/context_switch/main.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include + +using namespace std; + +// Settings for stack and benchmark +const unsigned int NUM_RUNS = 100000; +const unsigned int STACK_SIZE = 512 * 1; +const unsigned char MAGIC_NUMBER = (unsigned char) 0xAB; + +// Memory for custom stack and continuation semantics +unsigned char custom_stack[STACK_SIZE] = {0}; +jmp_buf buffer; + +// Example callback function and declaration of our assembly stack switching routine +extern "C" { +void custom_stack_callback(void *); + +void __attribute__ ((noinline)) callback() { + static volatile int tmp; + tmp = 0; // Force at least a single memory write +} +} + +long __attribute__ ((noinline)) measure_function_call() { + auto start_time = chrono::steady_clock::now(); + for (unsigned int i = 0; i < NUM_RUNS; i++) { + callback(); + } + auto end_time = chrono::steady_clock::now(); + return chrono::duration_cast(end_time - start_time).count(); +} + +long __attribute__ ((noinline)) measure_stack_switch() { + auto start_time = chrono::steady_clock::now(); + for (unsigned int i = 0; i < NUM_RUNS; i++) { + custom_stack_callback(&custom_stack[STACK_SIZE - 16]); + } + auto end_time = chrono::steady_clock::now(); + return chrono::duration_cast(end_time - start_time).count(); +} + +long __attribute__ ((noinline)) measure_continuation() { + auto start_time = chrono::steady_clock::now(); + for (unsigned int i = 0; i < NUM_RUNS; i++) { + if (setjmp(buffer) == 0) { + custom_stack_callback(&custom_stack[STACK_SIZE - 16]); + } + } + auto end_time = chrono::steady_clock::now(); + return chrono::duration_cast(end_time - start_time).count(); +} + +long __attribute__ ((noinline)) measure_continuation_and_jump() { + auto start_time = chrono::steady_clock::now(); + for (unsigned int i = 0; i < NUM_RUNS; i++) { + if (setjmp(buffer) == 0) { + custom_stack_callback(&custom_stack[STACK_SIZE - 16]); + longjmp(buffer, 1); + } + } + 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); + + auto time_cont_jump = measure_continuation_and_jump(); + auto time_cont = measure_continuation(); + auto time_stack = measure_stack_switch(); + auto time_func = measure_function_call(); + + for (unsigned int i = 0; i < STACK_SIZE; i++) { + if (custom_stack[i] != MAGIC_NUMBER) { + printf("Used stack size about %u bytes.\n\n\n", (STACK_SIZE - i)); + break; + } + } + + printf("Function Call : %10ld, %5.5f\n", time_func, ((float) time_func / NUM_RUNS)); + 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)); + + return 0; +} diff --git a/app/playground/CMakeLists.txt b/app/playground/CMakeLists.txt index 9482fb6..4c09641 100644 --- a/app/playground/CMakeLists.txt +++ b/app/playground/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable(playground main.cpp custom_stack_callback.s) +add_executable(playground main.cpp) # Example for adding the library to your app (as a cmake project dependency) target_link_libraries(playground) diff --git a/app/playground/main.cpp b/app/playground/main.cpp index 8022bfd..18d3906 100644 --- a/app/playground/main.cpp +++ b/app/playground/main.cpp @@ -1,75 +1,5 @@ -#include -#include -#include -#include - -using namespace std; - -// Settings for stack and benchmark -const unsigned int NUM_RUNS = 100000; -const unsigned int STACK_SIZE = 512 * 1; -const unsigned char MAGIC_NUMBER = (unsigned char) 0xAB; - -// Memory for custom stack and continuation semantics -unsigned char custom_stack[STACK_SIZE] = {0}; -jmp_buf buffer; - -// Example callback function and declaration of our assembly stack switching routine -extern "C" { -void custom_stack_callback(void *); - -void __attribute__ ((noinline)) callback() { - static volatile int tmp; - tmp = 0; // Force at least a single memory write -} -} - -long __attribute__ ((noinline)) measure_function_call() { - auto start_time = chrono::steady_clock::now(); - for (unsigned int i = 0; i < NUM_RUNS; i++) { - callback(); - } - auto end_time = chrono::steady_clock::now(); - return chrono::duration_cast(end_time - start_time).count(); -} - -long __attribute__ ((noinline)) measure_stack_switch() { - auto start_time = chrono::steady_clock::now(); - for (unsigned int i = 0; i < NUM_RUNS; i++) { - custom_stack_callback(&custom_stack[STACK_SIZE - 16]); - } - auto end_time = chrono::steady_clock::now(); - return chrono::duration_cast(end_time - start_time).count(); -} - -long __attribute__ ((noinline)) measure_continuation() { - auto start_time = chrono::steady_clock::now(); - for (unsigned int i = 0; i < NUM_RUNS; i++) { - if (setjmp(buffer) == 0) { - custom_stack_callback(&custom_stack[STACK_SIZE - 16]); - } - } - 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); - - auto time_cont = measure_continuation(); - auto time_stack = measure_stack_switch(); - auto time_func = measure_function_call(); - - for (unsigned int i = 0; i < STACK_SIZE; i++) { - if (custom_stack[i] != MAGIC_NUMBER) { - printf("Used stack size about %u bytes.\n\n\n", (STACK_SIZE - i)); - break; - } - } - - printf("Function Call : %10ld, %5.5f\n", time_func, ((float) time_func / NUM_RUNS)); - 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)); return 0; }