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

6 7 8 9
#include "fcontext/fcontext.h"

#include "fiber_call.h"

10 11 12
using namespace std;

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

// Memory for custom stack and continuation semantics
18
char custom_stack[STACK_SIZE] = {0};
19 20 21 22 23 24 25 26 27 28 29 30
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
}
}

31 32 33 34 35 36 37 38 39 40
long measure_loop() {
  auto start_time = chrono::steady_clock::now();
  volatile int tmp;
  for (unsigned int i = 0; i < NUM_RUNS; i++) {
    tmp = 0;
  }
  auto end_time = chrono::steady_clock::now();
  return chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();
}

41
long measure_function_call() {
42 43 44 45 46 47 48 49
  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();
}

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

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

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

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
void fcontext_callback_fast(fcontext_transfer_t transfer) {
  for (;;) {
    callback();
    jump_fcontext(transfer.ctx, nullptr);
  }
}

long measure_fcontext_fast() {
  fcontext_t context = make_fcontext(&custom_stack[STACK_SIZE], STACK_SIZE, &fcontext_callback_fast);

  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++) {
    fcontext_t context = make_fcontext(&custom_stack[STACK_SIZE], STACK_SIZE, &fcontext_callback_clean);
    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++) {
    fcontext_t context = make_fcontext(&custom_stack[STACK_SIZE], STACK_SIZE, &fcontext_callcc);
    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() {
  using namespace pls::internal::base;

133 134
  auto start_time = chrono::steady_clock::now();
  for (unsigned int i = 0; i < NUM_RUNS; i++) {
135 136 137 138
    fiber_call(custom_stack, STACK_SIZE, [](continuation_t continuation) {
      callback();
      return continuation;
    });
139 140 141 142 143
  }
  auto end_time = chrono::steady_clock::now();
  return chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count();
}

144 145 146 147 148 149 150
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();
151
  auto time_loop = measure_loop();
152 153 154 155 156 157 158
  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));
159
  printf("Simple Loop      : %10ld, %5.5f\n", time_loop, ((float) time_loop / NUM_RUNS));
160 161 162 163 164 165 166 167 168 169
  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));
170 171 172

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

  return 0;
}