main.cpp 6.07 KB
Newer Older
1 2 3 4
#include <cstdio>
#include <csetjmp>
#include <cstring>
#include <chrono>
5
#include <utility>
6

7 8
#include "fcontext/fcontext.h"

9 10
#include "context_switcher/context_switcher.h"
#include "context_switcher/continuation.h"
11

12 13 14
using namespace std;

// Settings for stack and benchmark
15 16 17
const size_t NUM_RUNS = 1000000;
const size_t STACK_SIZE = 512 * 1;
const char MAGIC_NUMBER = (unsigned char) 0xAB;
18 19

// Memory for custom stack and continuation semantics
20
char custom_stack_1[STACK_SIZE] = {0};
21 22 23 24 25 26 27 28 29
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
FritzFlorian committed
30
  (void) tmp;
31 32 33
}
}

34 35 36 37 38
long measure_loop() {
  auto start_time = chrono::steady_clock::now();
  volatile int tmp;
  for (unsigned int i = 0; i < NUM_RUNS; i++) {
    tmp = 0;
FritzFlorian committed
39
    (void) tmp;
40 41 42 43 44
  }
  auto end_time = chrono::steady_clock::now();
  return chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();
}

45
long measure_function_call() {
46 47 48 49 50 51 52 53
  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();
}

54
long measure_stack_switch() {
55 56
  auto start_time = chrono::steady_clock::now();
  for (unsigned int i = 0; i < NUM_RUNS; i++) {
57
    custom_stack_callback(&custom_stack_1[STACK_SIZE]);
58 59 60 61 62
  }
  auto end_time = chrono::steady_clock::now();
  return chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();
}

63
long measure_continuation() {
64 65 66
  auto start_time = chrono::steady_clock::now();
  for (unsigned int i = 0; i < NUM_RUNS; i++) {
    if (setjmp(buffer) == 0) {
67
      custom_stack_callback(&custom_stack_1[STACK_SIZE]);
68 69 70 71 72 73
    }
  }
  auto end_time = chrono::steady_clock::now();
  return chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();
}

74
long measure_continuation_and_jump() {
75 76 77
  auto start_time = chrono::steady_clock::now();
  for (unsigned int i = 0; i < NUM_RUNS; i++) {
    if (setjmp(buffer) == 0) {
78
      custom_stack_callback(&custom_stack_1[STACK_SIZE]);
79 80 81 82 83 84 85
      longjmp(buffer, 1);
    }
  }
  auto end_time = chrono::steady_clock::now();
  return chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();
}

86 87 88 89 90 91 92 93
void fcontext_callback_fast(fcontext_transfer_t transfer) {
  for (;;) {
    callback();
    jump_fcontext(transfer.ctx, nullptr);
  }
}

long measure_fcontext_fast() {
94
  fcontext_t context = make_fcontext(&custom_stack_1[STACK_SIZE], STACK_SIZE, &fcontext_callback_fast);
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

  auto start_time = chrono::steady_clock::now();
  for (unsigned int i = 0; i < NUM_RUNS; i++) {
    context = jump_fcontext(context, nullptr).ctx;
  }
  auto end_time = chrono::steady_clock::now();
  return chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();
}

void fcontext_callback_clean(fcontext_transfer_t transfer) {
  callback();
  jump_fcontext(transfer.ctx, nullptr);
}

long measure_fcontext_clean() {
  auto start_time = chrono::steady_clock::now();
  for (unsigned int i = 0; i < NUM_RUNS; i++) {
112
    fcontext_t context = make_fcontext(&custom_stack_1[STACK_SIZE], STACK_SIZE, &fcontext_callback_clean);
113 114 115 116 117 118 119 120 121 122 123 124 125 126
    jump_fcontext(context, nullptr);
  }
  auto end_time = chrono::steady_clock::now();
  return chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();
}

void fcontext_callcc(fcontext_transfer_t transfer) {
  callback();
  jump_fcontext(jump_fcontext(transfer.ctx, nullptr).ctx, nullptr);
}

long measure_fcontext_callcc() {
  auto start_time = chrono::steady_clock::now();
  for (unsigned int i = 0; i < NUM_RUNS; i++) {
127
    fcontext_t context = make_fcontext(&custom_stack_1[STACK_SIZE], STACK_SIZE, &fcontext_callcc);
128 129 130 131 132 133 134
    jump_fcontext(jump_fcontext(context, nullptr).ctx, nullptr);
  }
  auto end_time = chrono::steady_clock::now();
  return chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();
}

long measure_custom() {
135 136
  auto start_time = chrono::steady_clock::now();
  for (unsigned int i = 0; i < NUM_RUNS; i++) {
137
    context_switcher::enter_context(custom_stack_1, STACK_SIZE, [](context_switcher::continuation &&continuation) {
138
      callback();
139
      return std::move(continuation);
140
    });
141 142 143 144 145
  }
  auto end_time = chrono::steady_clock::now();
  return chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();
}

146
int main() {
147
  memset(custom_stack_1, MAGIC_NUMBER, STACK_SIZE);
148 149 150 151 152

  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();
153
  auto time_loop = measure_loop();
154 155 156 157 158 159 160
  auto time_fcontext_fast = measure_fcontext_fast();
  auto time_fcontext_clean = measure_fcontext_clean();
  auto time_fcontext_calcc = measure_fcontext_callcc();
  auto time_custom = measure_custom();

  printf("Base\n");
  printf("Function Call    : %10ld, %5.5f\n", time_func, ((float) time_func / NUM_RUNS));
161
  printf("Simple Loop      : %10ld, %5.5f\n", time_loop, ((float) time_loop / NUM_RUNS));
162 163 164 165 166 167 168 169 170 171
  printf("Longjmp\n");
  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("Boost\n");
  printf("FContext Fast    : %10ld, %5.5f\n", time_fcontext_fast, ((float) time_fcontext_fast / NUM_RUNS));
  printf("FContext Clean   : %10ld, %5.5f\n", time_fcontext_clean, ((float) time_fcontext_clean / NUM_RUNS));
  printf("FContext CallCC  : %10ld, %5.5f\n", time_fcontext_calcc, ((float) time_fcontext_calcc / NUM_RUNS));
  printf("Custom\n");
  printf("Custom Fast Call : %10ld, %5.5f\n", time_custom, ((float) time_custom / NUM_RUNS));
172 173

  for (unsigned int i = 0; i < STACK_SIZE; i++) {
174
    if (custom_stack_1[i] != MAGIC_NUMBER) {
175
      printf("\n\nUsed stack size about %lu bytes.\n", (STACK_SIZE - i));
176 177 178 179 180 181
      break;
    }
  }

  return 0;
}