#include <ffi.h>
#include <stdio.h>
#include <pthread.h>
#include <glib.h>

static __thread  ffi_type *arg_types[200];
static __thread  void *arg_values[200];

unsigned char
foo(unsigned int x, float y)
{
    unsigned char result = x - y;
    return result;
}

static inline void
alloca_test (void)
{
    ffi_type **arg_typesi;
    void **arg_valuesi;

    arg_typesi = g_alloca (sizeof (ffi_type*) * 2);
    arg_valuesi = g_alloca (sizeof (gpointer) * 2);

    ffi_cif cif;
    ffi_status status;
    ffi_arg result;

    arg_typesi[0] = &ffi_type_uint;
    arg_typesi[1] = &ffi_type_float;

    status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI,
        2, &ffi_type_uint8, arg_typesi);

    unsigned int arg1 = 42;
    float arg2 = 5.1;

    arg_valuesi[0] = &arg1;
    arg_valuesi[1] = &arg2;

    ffi_call(&cif, FFI_FN(foo), &result, arg_valuesi);
}

static inline void
tls_test (void)
{
    ffi_cif cif;
    ffi_status status;
    ffi_arg result;

    arg_types[0] = &ffi_type_uint;
    arg_types[1] = &ffi_type_float;

    status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI,
        2, &ffi_type_uint8, arg_types);

    unsigned int arg1 = 42;
    float arg2 = 5.1;

    arg_values[0] = &arg1;
    arg_values[1] = &arg2;

    ffi_call(&cif, FFI_FN(foo), &result, arg_values);
}




int
main(int argc, const char **argv)
{
  GTimer *t1 = g_timer_new ();
  GTimer *t2 = g_timer_new ();
  gdouble tt1, tt2;
  guint i = 0;

  // Elimenate cache differences
  for (i = 0; i< 10000000; i++) {
   alloca_test ();
   tls_test ();
  }

  g_timer_start (t1);
  for (i = 0; i< 100000000; i++)
     alloca_test ();
  g_timer_stop (t1);

  tt1 = g_timer_elapsed (t1, NULL);

  g_timer_start (t2);
  for (i = 0; i< 100000000; i++)
    tls_test();
  g_timer_stop (t2);

  tt2 = g_timer_elapsed (t2, NULL);

  g_print ("alloca: %f vs tls: %f\n", tt1, tt2);

}

