2010-05-05 16 views
5

Estaba pensando en un problema típico que es muy JIT-able, pero difícil de abordar con C. cruda. El escenario consiste en configurar una serie de indicadores de función que se están ejecutando ser "compuesto" (como en la composición de funciones matemáticas) una vez en el tiempo de ejecución y luego llamado muchas veces."Inline" (tipo de) funciones en tiempo de ejecución en C

Hacerlo de la manera obvia implica muchas llamadas virtuales, que son caras, y si hay suficientes funciones anidadas para llenar completamente la tabla de predicción de la bifurcación de la CPU, entonces el rendimiento disminuirá considerablemente.

En un lenguaje como Lisp, probablemente podría procesar el código y sustituir la llamada "virtual" por el contenido real de las funciones y luego llamar al compile para tener una versión optimizada, pero parece muy raro y propenso a errores en C, y usar C es un requisito para este problema ;-)

Entonces, ¿sabes si hay una forma estándar, portátil y segura de lograr esto en C?

Saludos

Respuesta

6

Es posible que desee ver en LLVM. Tienen una biblioteca que permite el código JITing (y mucho más cosas) de C, es compatible con muchas plataformas y es un proyecto de código abierto: http://llvm.org/

+1

Sí, es una opción que estaba considerando, pero yo estaba pensando en algo más sencillo, no creo Necesito todo el JIT, solo pegar algunas funciones juntas como si fueran simplemente una gran función. Gracias de todos modos :-) – fortran

1

tengo dos sugerencias sobre la eliminación de sus llamadas a funciones virtuales, si necesario para el rendimiento. En aras de la ilustración, suponga que tiene una función que toma un puntero a función como un argumento:

void my_algorithm(int (*func)(...), ...) 
{ 
    /* ... */ 
} 

Y también supongamos que se sabe de antemano todos los posibles valores del puntero de función puede tomar. Por ejemplo:

my_algorithm(func_1, ...); 
//... 
my_algorithm(func_2, ...); 

Primera convertir su my_algorithm originales() en una macro:

#define MY_ALGORITHM(func, ...)  \ 
{          \ 
    /* ... */       \ 
} 

vuelva a grabar my_algorithm() como:

extern int func_1(...); 
extern int func_2(...); 

void my_algorithm(int (*func)(...), ...) 
{ 
    if (func == func_1) 
     MY_ALGORITHM(func_1, ...) 
    else if (func == func_2) 
     MY_ALGORITHM(func_2, ...) 
    else 
     assert(0 && "Unexpected function arg to my_algorithm()"); 
} 

Esto, por supuesto duplicar el tamaño de el archivo de objeto compilado. Y, en la superficie, elimina solo un nivel de indirección. Pero si func_1 y/o func_2 están en línea, puede obtener una aceleración considerable.

e incluso se puede macros 'pass', como en:

#define HYPOT_Y(x) hypot(x, y) 
MY_ALGORITHM(HYPOT_Y, ...);  //assumes y is known 

La segunda sugerencia es una variación de este, usando macros X (http://en.wikipedia.org/wiki/C_preprocessor#X-Macros). En lugar de #define, coloque el cuerpo del my_algorithm original() en un archivo separado, my_algorithm.h. Vuelva a grabar my_algorithm() como:

void my_algorithm(int (*func)(...), ...) 
{ 
    if (func == func_1) 
     #define func func_1 
     #include "my_algorithm.h" 
     #undef func 
    else if (func == func_2) 
     #define func func_2 
     #include "my_algorithm.h" 
     #undef func 
    else 
     assert(0 && "Unexpected function arg to my_algorithm()"); 
} 

probablemente volvería a usar macros X si el código es más que un par de docenas de líneas. Sus ventajas incluyen (sin juego de palabras):

  • No hay barras invertidas feas
  • Depuración más fácil (por ejemplo, de nuevo trazas y de un solo paso a paso).

Esto es todo el estándar C. Pero más bien de la vieja escuela.

+1

Si hace esto, por favor use una declaración switch y pase un entero/enum en lugar de usar un montón de if/else si/etc. Los interruptores son TAN mucho más rápidos. – SoapBox

+0

Los interruptores en C funcionan solo en tipos integrales. –

+0

Puede activar una constante en lugar del puntero de función. No tome la dirección de la función en absoluto si no la necesita, obliga al compilador a proporcionar una copia no en línea de la función solo para dar su puntero. Además, la alineación (con la mayoría de los compiladores de C) no funcionará con extern, ya que las funciones deben incluirse en la misma unidad de compilación. – wump

0

Si estás bien con que soporta solamente x86, podría intentar la incorporación de TCC:

http://bellard.org/tcc/

+0

lamentablemente necesito que el código sea portable a varias plataformas :-(gracias por el enlace, ¡podría ser interesante mirar! – fortran

+1

que LLVM es su plataforma. No es tan malo, después de todo, y es fácil de usar a través de una API C simple. Otra alternativa es implementar un código enhebrado Forth intepreter en C (usando una extensión gCC Goto calculada), y generar código para ello. Debe ser 2-6 veces más lento que un JIT de descenso, pero trivial y absolutamente portátil. Es lo suficientemente bueno para simplemente pegar funciones juntas. –

Cuestiones relacionadas