2010-10-03 11 views

Respuesta

17

(Nota: Vamos a suponer que por "decodificar y despacho" quiere decir un intérprete basada en conmutadores.)

La diferencia entre una basada en conmutadores y un intérprete de roscado en el tiempo de ejecución es, básicamente, el número de saltos que se realizan.

En un intérprete basado en conmutador, las instrucciones se decodifican en una ubicación central y, en función del resultado de la decodificación, se realiza un salto al fragmento de código que maneja la instrucción descodificada. Una vez que esa parte del código ha terminado de interpretar la instrucción, salta de vuelta al código de descodificación centralizado, que continúa con la siguiente instrucción. Esto significa que (al menos) se realizan dos saltos por instrucción interpretada. El siguiente fragmento de código C ilustra lo que un intérprete por ejemplo podría ser:

typedef enum { 
    add, /* ... */ 
} instruction_t; 

void interpret() { 
    static instruction_t program[] = { add /* ... */ }; 
    instruction_t* pc = program; 
    int* sp = ...; /* stack pointer */ 
    for (;;) { 
    switch (*pc++) { 
     case add: 
     sp[1] += sp[0]; 
     sp++; 
     break; 
     /* ... other instructions */ 
    } 
    } 
} 

En un intérprete de roscado, el código de decodificación no es centralizado, sino más bien duplicado al final de cada pieza de código que se encarga de una instrucción. Esto significa que una vez que se ha interpretado una instrucción, en lugar de volver a saltar a un código de descodificación centralizado, el intérprete decodifica la siguiente instrucción e inmediatamente salta a ella. Implementar el código de subprocesamiento de manera eficiente en ANSI-C no es realmente posible, pero la extensión "goto computada" de GCC funciona muy bien para eso. He aquí una versión roscada del intérprete anterior:

void interpret() { 
    void* program[] = { &&l_add, /* ... */ }; 
    int* sp = ...; 
    void** pc = program; 
    goto **pc; /* jump to first instruction */ 
l_add: 
    sp[1] += sp[0]; 
    ++sp; 
    goto **(++pc); /* jump to next instruction */ 
    /* ... other instructions */ 
} 

Además de ahorrar un salto, tales intérpretes roscados son también más eficiente porque el salto indirecto replicado (a la siguiente instrucción) puede predecirse mejor por las CPU modernas. Anton Ertl tiene algunos documentos interesantes en his home page, especialmente el llamado "La estructura y el rendimiento de intérpretes eficientes", a partir del cual se adaptaron las piezas de código anteriores.

Cuestiones relacionadas