/* 
 *      threaded.c --
 *
 *      A small study on implementation techniques for threaded code.
 *      
 *      As example a small program stacking two values on to the stack
 *      and adding the result is taken (example from wikipedia). This
 *      program uses the abstract machine instructions PUSH_A, PUSH_B,
 *      ADD, and END. These instructions are coded in different ways:
 *      
 *      * switch_threading(): realizing instructions as C enum
 *        values, the program is represented as an array of enums,
 *        placed in a large surrounding switch statement.
 *
 *      * call_threading(): realizing instructions as C functions, the
 *        program is represented an array of C functions.
 *
 *      * label_threading(): realizing instructions as labels, the
 *        program is represented an array of labels.
 *
 *      The test show, that label_threading is clearly the winner in
 *      terms of performance, but this depends on a non standard C
 *      extension, which is implemented in at least three different
 *      compilers (supported on GCC, clang and IBM's XL C/C++). It
 *      would be interesting to define the instructions in some
 *      e.g. scripting language and to produce different
 *      implementations from the same source to address the
 *      portability issue.
 *
 *      Most probably, one needs a larger program-code with more
 *      instructions to provide more meaningful results.
 *
 *      Compile e.g. with:
 *           cc -O3 -Wall threaded.c   -o threaded
 *
 *      Gustaf Neumann (Nov 2011)
 */

#include <stdio.h>
#include <sys/time.h>

/*
 *----------------------------------------------------------------------
 * DiffTime --
 *
 *      Compute the time difference between two timevals.
 *
 *----------------------------------------------------------------------
 */

static void
DiffTime(struct timeval *t1, struct timeval *t0, struct timeval *diffPtr) {
  diffPtr->tv_sec = t1->tv_sec - t0->tv_sec;
  diffPtr->tv_usec = t1->tv_usec - t0->tv_usec;
  if (diffPtr->tv_usec < 0) {
    diffPtr->tv_sec += (diffPtr->tv_usec / 1000000L) - 1;
    diffPtr->tv_usec = (diffPtr->tv_usec % 1000000L) + 1000000L;
  } else if (diffPtr->tv_usec > 1000000L) {
    diffPtr->tv_sec += diffPtr->tv_usec / 1000000L;
    diffPtr->tv_usec = diffPtr->tv_usec % 1000000L;
  }
}

/*
 *----------------------------------------------------------------------
 * timeit --
 *
 *      Run a function multiple times and measure the performance.
 *
 *----------------------------------------------------------------------
 */

static void
timeit( void (* fn)() ) {
  struct timeval start, end, diff;
  int i;

  gettimeofday(&start, NULL);

  for (i=0; i<10000000; i++) {
    (*fn)();
  }

  gettimeofday(&end, NULL);
  DiffTime(&end, &start, &diff);
  printf("%d seconds, %d usec\n", (int) diff.tv_sec, (int) diff.tv_usec);
}


int *sp;
int stack[100];

/*
 *----------------------------------------------------------------------
 * switch_threading --
 *
 *      Define an enum for the instructions and use a switch to select
 *      the instructions.
 *
 *----------------------------------------------------------------------
 */
void
switch_threading() {
  typedef enum {INST_PUSH_A, INST_PUSH_B, INST_ADD, INST_END} InstEnum;
  static InstEnum mycode[] = {INST_PUSH_A, INST_PUSH_B, INST_ADD, INST_END};
  int a, b;
  InstEnum *ip;

  ip = &mycode[0];
  sp = &stack[0];

  for (;;) {
    switch (*ip++) {
    case INST_PUSH_A:
      *sp++ = 100;
      continue;
    case INST_PUSH_B:
      *sp++ = 200;
      continue;      
    case INST_ADD: 
      a = *--sp;
      b = *--sp;
      *sp++ = a + b;
      continue;
    case INST_END: 
      //fprintf(stderr, "end %d\n", *(sp-1));
      return;
    }
  }
}

/*
 *----------------------------------------------------------------------
 * call_threading --
 *
 *      Define for every instruction a function, the program consists of
 *      an array of function pointers.
 *
 *----------------------------------------------------------------------
 */
typedef void (* InstFn)();

void pushA () { *sp++ = 100; }
void pushB () { *sp++ = 200; }
void add   () { int a = *--sp; int b = *--sp; *sp++ = a + b; }

void
call_threading() {
  static InstFn mycode[] = {&pushA, &pushB, &add, NULL};
  InstFn *ip;

  sp = &stack[0];
  ip = &mycode[0];

  while (*ip) {
    (*ip++)();
  }

  //fprintf(stderr, "end %d\n", *(sp-1));
}

/*
 *----------------------------------------------------------------------
 * label_threading --
 *
 *      Define for every instruction a label, the code is a sequence of
 *      labels. This works with gcc, clang and IBM's XL C, but not on
 *      every compiler.
 *
 *----------------------------------------------------------------------
 */
typedef void (* InstLabel)();

void
label_threading() {
  static InstLabel mycode[] = {&&INST_PUSH_A, &&INST_PUSH_B, &&INST_ADD, &&INST_END};
  InstLabel *ip;
  int a, b;

  sp = &stack[0];
  ip = &mycode[0];

 INST_PUSH_A:
  *sp++ = 100;
  goto **ip++;

 INST_PUSH_B:
  *sp++ = 200;
  goto **ip++;

 INST_ADD: 
  a = *--sp;
  b = *--sp;
  *sp++ = a + b;  
  goto **ip++;
  
 INST_END: 
  //fprintf(stderr, "end %d\n", *(sp-1));
  return;
}

/*
 *----------------------------------------------------------------------
 * main --
 *
 *      Just call the testcases with timing.
 *
 *----------------------------------------------------------------------
 */

int
main() {

  timeit( switch_threading );
  timeit( call_threading   );
  timeit( label_threading  );

  return 0;
}