Leistungsverbesserungsstrategien für VM / Interpreter?

Ich habe eine einfache VM in C geschrieben, mit einem einfachen Befehlswechsel, ohne irgendwelche Befehle zu dekodieren, aber die Leistung ist schrecklich.

Für einfache aritmetische Operationen ist die VM ungefähr 4000-mal langsamer als nativer C-Code für dieselben Operationen. Ich habe mit einer Gruppe von Arrays mit einer Länge von 10 Millionen getestet, wobei das erste aus den Programmanweisungen, zufälligen + - * / Operationen, 2 Arrays mit zufälligen ganzen Zahlen und dem dritten Array als Operationszielspeicher besteht.

Ich hatte damit gerechnet, dass die Rechenleistung um das 3-4-fache sinken würde, so dass mich 4000x wirklich umgehauen hat. Selbst die am langsamsten gedolmetschten Sprachen scheinen eine höhere Leistung zu bieten. Wo also gehe ich mit meinem Ansatz schief und wie kann ich die Leistung verbessern, ohne auf die JIT-Kompilierung auf Maschinencode zurückzugreifen?

Die Implementierung ist ... im Grunde die einfachste, die ich mir einfallen lassen konnte:

begin:
    {
        switch (*(op+(c++)))
        {
        case 0:
            add(in1+c, in2+c, out+c); goto begin;

        case 1:
            sub(in1+c, in2+c, out+c); goto begin;

        case 2:
            mul(in1+c, in2+c, out+c); goto begin;

        case 3:
            div(in1+c, in2+c, out+c); goto begin;

        case 4:
            cout << "end of program" << endl;
            goto end;

        default:
            cout << "ERROR!!!" << endl;

        }
    }

end:

UPDATE: Ich spielte mit der Länge des Programms, als ich bemerkte, dass der QElapsedTimer, den ich zum Profilieren verwendete, tatsächlich defekt war. Jetzt benutze ich die Funktion clock () von und dementsprechend läuft das berechnete goto tatsächlich auf dem Niveau des nativen Codes, vielleicht ein bisschen niedriger. Ist das Ergebnis echt ??? Hier ist die vollständige Quelle (es ist hässlich, ich weiß, es ist nur zum Testen):

#include <QtGlobal>
#include <iostream>
#include <stdio.h>
#include <ctime>

using namespace std;

#define LENGTH 70000000

void add(int & a, int & b, int & r) {r = a * b;}
void sub(int & a, int & b, int & r) {r = a - b;}
void mul(int & a, int & b, int & r) {r = a * b;}
void div(int & a, int & b, int & r) {r = a / b;}

int main()
{
    char * op = new char[LENGTH];
    int * in1 = new int[LENGTH];
    int * in2 = new int[LENGTH];
    int * out = new int[LENGTH];

    for (int i = 0; i < LENGTH; ++i)
    {
        *(op+i) = i % 4;
        *(in1+i) = qrand();
        *(in2+i) = qrand()+1;
    }

    *(op+LENGTH-1) = 4; // end of program


    long long  sClock, fClock;


    unsigned int c = 0;
    sClock = clock();

    cout << "Program begins" << endl;

    static void* table[] = {
        &&do_add,
        &&do_sub,
        &&do_mul,
        &&do_div,
        &&do_end,
        &&do_err,
        &&do_fin};

#define jump() goto *table[op[c++]]

    jump();
do_add:
    add(in1[c], in2[c], out[c]); jump();
do_sub:
    sub(in1[c], in2[c], out[c]); jump();
do_mul:
    mul(in1[c], in2[c], out[c]); jump();
do_div:
    div(in1[c], in2[c], out[c]); jump();
do_end:
    cout << "end of program" << endl; goto *table[6];
do_err:
    cout << "ERROR!!!" << endl; goto *table[6];
do_fin:

    fClock = clock();
    cout << fClock - sClock << endl;

    delete [] op;
    delete [] in1;
    delete [] in2;
    delete [] out;

    in1 = new int[LENGTH];
    in2 = new int[LENGTH];
    out = new int[LENGTH];

    for (int i = 0; i < LENGTH; ++i)
    {
        *(in1+i) = qrand();
        *(in2+i) = qrand()+1;
    }

    cout << "Native begins" << endl;

    sClock = clock();

    for (int i = 0; i < LENGTH; i += 4)
    {

        *(out+i) = *(in1+i) + *(in2+i);
        *(out+i+1) = *(in1+i+1) - *(in2+i+1);
        *(out+i+2) = *(in1+i+2) * *(in2+i+2);
        *(out+i+3) = *(in1+i+3) / *(in2+i+3);
    }

    fClock = clock();
    cout << fClock - sClock << endl;

    delete [] in1;
    delete [] in2;
    delete [] out;

    return 0;
}

Antworten auf die Frage(1)

Ihre Antwort auf die Frage