main.cpp 3.42 KB
Newer Older
1 2 3 4 5 6 7 8 9
#include <cstdio>
#include <csetjmp>
#include <cstring>
#include <chrono>

using namespace std;

// Settings for stack and benchmark
const unsigned int NUM_RUNS = 100000;
10
const unsigned int STACK_SIZE = 512 * 8;
11 12 13 14 15 16 17 18 19 20
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 *);

21 22 23
void fiber_call(void *);
void fiber_continue(void *);

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
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<chrono::nanoseconds>(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<chrono::nanoseconds>(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<chrono::nanoseconds>(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<chrono::nanoseconds>(end_time - start_time).count();
}

71 72 73 74 75 76 77 78 79 80
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<chrono::nanoseconds>(end_time - start_time).count();
}

81 82 83 84 85 86 87
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();
88
  auto time_fiber = measure_fiber_call();
89 90 91 92 93 94 95 96 97 98 99 100

  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));
101
  printf("Fiber Call       : %10ld, %5.5f\n", time_fiber, ((float) time_fiber / NUM_RUNS));
102 103 104

  return 0;
}