I was noodling around with a couple different VM implementations
and thought I would ask other people if they were doing the same.
It would be nice to divide and conquer the exploration of different
implementations.

I looked into 3 different dispatching techniques. Here's the list
with timings on a 600 MHz Pentium III running Linux and gcc 2.95:

                                 -g            -O
Switch/case                    6.0 sec       2.0 sec
Computed goto (gcc feature)    6.1 sec       1.4 sec
Function call                 11.9 sec      11.8 sec

Both the switch and goto implementations put all the opcodes into
a single function body and just bounce around inside the function.
The function call implementation runs something similar to perl 5's
tiny while loop dispatcher with each opcode implemented as a
separate function.

The timings above were collected for more than 200 million VM
instructions. That's a lot faster than what I was expecting -- and
makes me wonder whether we need to do something platform-specific
like a TIL to improve performance. My testing code is way too small
to indicate problems with data cache vs. instruction cache, but I
suspect that that issue is the only issue that will really drive
us towards a TIL implementation.

I'll put together a TIL (Intel -- same machine as above) for my toy
VM and run some timings. If anybody else has already done this
please let us know!

I'll also try to expand my toy VM to something a little more
practical so we can start to see memory delays. Again, if anybody
has done work in this area, I'm sure everybody on the list would
love to hear about it. A VM with "registers" might be really
interesting too -- especially in a computed goto or TIL machine.

The code is given as a MIME attachment. BTW, are MIME attachments
ok on the list? I strongly prefer plain-text, but for attachments
there aren't many good alternatives.

- Ken
1 0
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2
1 10
2

/* This code is in the public domain. Ken Fox, Oct 2000 */

#define USE_GOTO

#include <stdlib.h>
#include <stdio.h>

typedef union {
    int i;
    void *p;
} cell;

void load(char *filename, cell *code)
{
    FILE *input;
    cell *pc = code;

    if (input = fopen(filename, "r")) {
        int i;
        while (fscanf(input, " %d ", &i) != EOF) {
            pc->i = i;
            ++pc;
        }
        fclose(input);
    }

    pc->i = 0;
}

void dump(cell *code)
{
    cell *pc = code;

    printf("byte code:\n");
    while (pc->i) {
        printf("  %5d\n", pc->i);
        ++pc;
    }
}

enum {
    STOP = 0,
    PUSH = 1,
    ADD = 2,
    PRINT = 3
};

int count_ops(cell *code)
{
    int icount = 0;

    cell *pc = code;

op: switch (pc->i) {
        case STOP:
            return icount;

        case PUSH:
            ++icount;
            pc += 2;
            goto op;

        case ADD:
            ++icount;
            pc += 1;
            goto op;

        case PRINT:
            ++icount;
            pc += 1;
            goto op;
    }
}

void fixup(cell *code, void *ops[])
{
    cell *pc = code;

op: switch (pc->i) {
        case STOP:
            pc->p = ops[pc->i];
            return;

        case PUSH:
            pc->p = ops[pc->i];
            pc += 2;
            goto op;

        case ADD:
            pc->p = ops[pc->i];
            pc += 1;
            goto op;

        case PRINT:
            pc->p = ops[pc->i];
            pc += 1;
            goto op;
    }
}

#ifdef USE_SWITCH

void execute(cell *code)
{
    static int stack[1000];
    cell *pc = code;
    int *sp = stack;
    int x, y;

op: switch (pc++->i) {
        case STOP:
            return;

        case PUSH:
            *sp++ = pc++->i;
            goto op;

        case ADD:
            y = *--sp;
            x = *--sp;
            *sp++ = x + y;
            goto op;

        case PRINT:
            printf("--> %d\n", *--sp);
            goto op;
    }
}

#else
#ifdef USE_FUNCALL

typedef void *(*op)();

cell *pc;
int *sp;

void *op_stop()
{
    return 0;
}

void *op_push()
{
    *sp++ = pc[1].i;
    return pc + 2;
}

void *op_add()
{
    int y = *--sp;
    int x = *--sp;
    *sp++ = x + y;
    return pc + 1;
}

void *op_print()
{
    printf("--> %d\n", *--sp);
    return pc + 1;
}

void execute(cell *code)
{
    static void *ops[] = { op_stop, op_push, op_add, op_print };
    static int stack[1000];

    if (code->i > 0 && code->i < 255) {
        fixup(code, ops);
    }

    pc = code;
    sp = stack;

    while (pc = ((op)(pc->p))())
        ;
}

#else
#ifdef USE_GOTO

void execute(cell *code)
{
    static void *ops[] = { &&op_stop, &&op_push, &&op_add, &&op_print };
    static int stack[1000];
    cell *pc = code;
    int *sp = stack;
    int x, y;

    if (code->i > 0 && code->i < 255) {
        fixup(code, ops);
    }

    goto *((pc++)->p);

op_stop:
    return;

op_push:
    *sp++ = pc++->i;
    goto *((pc++)->p);

op_add:
    y = *--sp;
    x = *--sp;
    *sp++ = x + y;
    goto *((pc++)->p);

op_print:
    printf("--> %d\n", *--sp);
    goto *((pc++)->p);
}

#endif
#endif
#endif

int main(int argc, char *argv[])
{
    static cell code[10000];
    int n;

    if (argc != 2) {
        printf("vm-test [byte-code-file]\n");
        return 1;
    }

    load(argv[1], code);

    printf("executing %d ops.\n", 100000 * count_ops(code));
    for (n = 0; n < 100000; ++n) {
        execute(code);
    }

    return 0;
}

Reply via email to