/* Benchmark some different ways of calling methods in C.
 * Geoff Richards, 2004-01-18.
 * You may do what you want with this code.
 */

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

#define HOW_MANY 100000000
#define NUM_TURNS 100
#define NUM_METHODS 3

struct Class0;
typedef int (*Class0Method) (struct Class0 *obj, int i);
int class0_method (struct Class0 *obj, int i);

struct Class0 {
    Class0Method fun;
    Class0Method fun_unused1;
    Class0Method fun_unused2;
    int foo;
    double bar;
};
typedef struct Class0 Class0;


struct Class1;
typedef int (*Class1Method) (struct Class1 *obj, int i);
int class1_method (struct Class1 *obj, int i);

void *class1_vtbl[3] = { (void *) class1_method, 0, 0 };
struct Class1 {
    void **vtbl;
    int foo;
    double bar;
};
typedef struct Class1 Class1;


struct Class2;
typedef int (*Class2Method) (struct Class2 *obj, int i);
int class2_method (struct Class2 *obj, int i);

struct Class2Vtbl {
    Class2Method method1;
    Class2Method method2;
    Class2Method method3;
};
typedef struct Class2Vtbl Class2Vtbl;
Class2Vtbl class2_vtbl;
struct Class2 {
    Class2Vtbl *vtbl;
    int foo;
    double bar;
};
typedef struct Class2 Class2;


int
main ()
{
    int turn, method, i;
    int useless;
    struct timeval start, end;
    double times[NUM_METHODS];
    Class0 object0;
    Class1 object1;
    Class2 object2;

    object0.fun = class0_method;
    object1.vtbl = class1_vtbl;
    class2_vtbl.method1 = class2_method;
    object2.vtbl = &class2_vtbl;

    for (method = 0; method < NUM_METHODS; ++method)
        times[method] = 0;

    for (turn = 0; turn < NUM_TURNS; ++turn) {
        for (method = 0; method < NUM_METHODS; ++method) {
            if (gettimeofday(&start, 0)) {
                fprintf(stderr, "Error getting time.\n");
                return 3;
            }
            assert(start.tv_usec >= 0 && start.tv_usec < 1000000);

            if (method == 0) {
                for (i = 0; i < HOW_MANY; ++i) {
                    useless = object0.fun(&object0, i);
                    assert(useless == i);
                }
            }
            else if (method == 1) {
                for (i = 0; i < HOW_MANY; ++i) {
                    useless = ((Class1Method) object1.vtbl[0])(&object1, i);
                    assert(useless == i);
                }
            }
            else {
                for (i = 0; i < HOW_MANY; ++i) {
                    useless = object2.vtbl->method1(&object2, i);
                    assert(useless == i);
                }
            }

            if (gettimeofday(&end, 0)) {
                fprintf(stderr, "Error getting time.\n");
                return 3;
            }
            assert(end.tv_usec >= 0 && end.tv_usec < 1000000);

            times[method] += end.tv_sec - start.tv_sec +
                             (end.tv_usec - start.tv_usec) / 1000000.0;
        }
    }

    for (method = 0; method < NUM_METHODS; ++method)
        printf("Method %d: %g seconds\n", method, times[method]);

    return 0;
}


int
class0_method (struct Class0 *obj, int i)
{
    assert(obj);
    return i;
}

int
class1_method (struct Class1 *obj, int i)
{
    assert(obj);
    return i;
}

int
class2_method (struct Class2 *obj, int i)
{
    assert(obj);
    return i;
}

/* vi:set ts=4 sw=4 expandtab: */
