2009-04-23 7 views
20

¿Alguien está usando trucos JIT para mejorar el rendimiento del tiempo de ejecución de lenguajes compilados estáticamente como C++? Parece que el análisis de hotspot y la predicción de bifurcación basada en observaciones realizadas durante el tiempo de ejecución podrían mejorar el rendimiento de cualquier código, pero tal vez haya alguna razón estratégica fundamental por la que realizar dichas observaciones e implementar cambios durante el tiempo de ejecución solo sea posible en máquinas virtuales. Recuerdo claramente escuchar por casualidad a los escritores de compiladores de C++ murmurar "también puedes hacerlo para programas escritos en C++" mientras escucho a entusiastas del lenguaje dinámico hablar sobre recopilar estadísticas y reorganizar el código, pero mi web busca evidencias para apoyar esta memoria.Optimización del tiempo de ejecución de idiomas estáticos: JIT para C++?

Respuesta

32

La optimización guiada de perfil es diferente de la optimización de tiempo de ejecución. La optimización aún se realiza sin conexión, según la información de perfil, pero una vez que se envía el binario no hay una optimización continua, por lo que si los patrones de uso de la fase de optimización guiada por perfil no reflejan exactamente el uso en el mundo real, los resultados serán imperfecto, y el programa tampoco se adaptará a los diferentes patrones de uso.

Puede que esté interesado en buscar información en HP's Dynamo, aunque ese sistema se centró en la traducción nativa binaria -> binaria nativa, aunque como C++ se compila casi exclusivamente en código nativo, supongo que eso es exactamente lo que está buscando.

Es posible que desee echar un vistazo a LLVM, que es un marco de compilación y una representación intermedia que admite la compilación JIT y la optimización de tiempo de ejecución, aunque no estoy seguro de si hay algún tiempo de ejecución basado en LLVM que pueda compilar C++ y ejecutar + runtime optimizarlo aún.

5

visual studio tiene una opción para hacer perfiles de tiempo de ejecución que luego se puede utilizar para la optimización del código.

"optimización del perfil de guiado"

+0

-1 esto no es una optimización del tiempo de ejecución, todo está estáticamente * basado * en el comportamiento del tiempo de ejecución, se atornilla si el comportamiento cambia en "otro" tiempo de ejecución – Quonux

5

Microsoft Visual Studio llama a esto "profile guided optimization"; puede obtener más información al MSDN. Básicamente, ejecuta el programa un montón de veces con un generador de perfiles adjunto para registrar sus puntos de acceso y otras características de rendimiento, y luego puede alimentar la salida del generador de perfiles en el compilador para obtener las optimizaciones adecuadas.

+0

Umm ... ¿Hay otros gérmenes escondidos sin licencia allí? – PedroMorgan

10

Hice ese tipo de optimización bastante en los últimos años. Fue para una API de renderizado gráfico que he implementado. Dado que la API definía varios miles de modos de dibujo diferentes como función de propósito general, era un proceso lento.

Terminé escribiendo mi propio pequeño compilador Jit para un lenguaje específico de dominio (muy parecido al asm, pero con algunas estructuras de control de alto nivel y variables locales incluidas).

La mejora en el rendimiento que obtuve estuvo entre el factor 10 y 60 (dependía de la complejidad del código compilado), por lo que el trabajo adicional valió la pena a lo grande.

En la PC no comenzaría a escribir mi propio compilador de jit pero usaría LIBJIT o LLVM para la compilación de jit. No fue posible en mi caso debido al hecho de que estaba trabajando en un procesador incrustado no convencional que no es compatible con LIBJIT/LLVM, así que tuve que inventar el mío propio.

+5

++ Otra forma en que he visto esto hecho es cruda pero efectiva. Asigne un bloque de pila y genere sobre la marcha una rutina de máquina-lenguaje especializada y llámelo. Soy un gran fan de la generación de código. –

2

Pregunta razonable, pero con una premisa dudosa.

Como en la respuesta de Nils, a veces "optimización" significa "optimización de bajo nivel", que es un buen tema en sí mismo.

Sin embargo, se basa en el concepto de un "punto caliente", que no está ni remotamente relacionado con la relevancia que comúnmente se le da.

Definición: un punto caliente es una pequeña región de código donde el contador de programa de un proceso pasa un gran porcentaje de su tiempo.

Si hay un punto caliente, como un lazo interno apretado que ocupa mucho tiempo, vale la pena tratar de optimizar en el nivel bajo, si está en el código que usted controla (es decir, no en un tercero). biblioteca de fiestas).

Supongamos ahora que el bucle interno contiene una llamada a una función, cualquier función. Ahora es poco probable que el contador del programa se encuentre allí, porque es más probable que esté en la función. Entonces, aunque el código puede ser un desperdicio, ya no es un punto caliente.

Hay muchas formas comunes de hacer que el software sea lento, de los cuales los puntos conflictivos son uno. Sin embargo, en mi experiencia, esa es la única de la que la mayoría de los programadores saben, y la única a la que se aplica la optimización de bajo nivel.

See this.

+0

¿Le das alguna idea de que (algunas técnicas de JIT (en términos generales) hacen que los programas Java, C#, etc. sean más rápidos? Si es así, ¿conoce algún esfuerzo para aplicar esas técnicas a los idiomas compilados estáticamente o, como alternativa, una razón por la cual los lenguajes compilados estáticamente no pueden beneficiarse de estas técnicas? –

+0

@Thomas: ¿Más rápido que qué? ¿Bytecode interpretado? Por supuesto. ¿Otro código compilado estático? Marginal, tal vez, si se puede usar el conocimiento del tiempo de ejecución. De todos modos, se trata de buscar llaves debajo de la farola. Las teclas tienen un mayor rendimiento y la farola es una optimización de bajo nivel. –

3

creo LLVM intentos de hacer algo de esto. Intenta optimizar a lo largo de toda la vida útil del programa (tiempo de compilación, tiempo de enlace y tiempo de ejecución).

+0

LLVM tiene la intención de hacer esto en el futuro, pero solo recientemente obtuvieron su compilador C++ en un estado confiable y no hay JIT ni optimización en tiempo de ejecución. Sin embargo, puede hacer una optimización guiada por perfiles. – BinarySplit

7

La respuesta es más probable: nadie hizo más que PGO para C++ porque los beneficios son probablemente imperceptibles.

Déjenme detallar: JIT los motores/tiempos de ejecución tienen ventajas y desventajas desde su punto de vista del desarrollador: tienen más información en tiempo de ejecución pero tienen poco tiempo para analizar. Algunas optimizaciones son realmente costosas y es poco probable que se vea sin un gran impacto en la hora de inicio: desenrollado de bucle, auto-vectorización (que en la mayoría de los casos también se basa en el despliegue del bucle), selección de instrucciones (para usar SSE4.1 para la CPU que usa SSE4.1) combinada con la programación de instrucciones y el reordenamiento (para usar CPUs súper-escalares mejores). Este tipo de optimizaciones se combina muy bien con el código tipo C (que es accesible desde C++).

La única arquitectura compilador en toda regla para hacer la compilación avanzada (por lo que yo sé) es el Java hotspot compilación y arquitecturas con principios similares utilizando la compilación niveles (sistemas de Java Azul 's, lo popular a lo día JaegerMonkey motor JS).

Pero uno de los mayores optimización en tiempo de ejecución es el siguiente:

polimórfica almacenamiento en caché en línea (lo que significa que si se ejecuta el primer bucle con algunos tipos, la segunda vez, se especializó en el código del bucle tipos que fueron del ciclo anterior, y el JIT pondrá un guardia y pondrá como rama predeterminada los tipos en línea, y en base a ello, desde este formulario especializado utilizando un SSA basado en motor aplicará doblado/propagación constantes , en línea, optimizaciones de eliminación de código muerto, y depende de cómo "avanzado" el JIT es, hará una asignación de registro de CPU mejorada o menos mejorada.) Como puede observar, JIT (puntos de acceso) mejorará principalmente el código de ramal, y con la información de tiempo de ejecución mejorará un código de C++, pero un compilador estático, teniendo a su lado el tiempo para hacer análisis, reordenamiento de instrucciones , para bucles simples, es probable que obtenga un rendimiento un poco mejor. Además, normalmente, el código C++, las áreas que deben ser rápidas no suelen ser OOP, por lo que la información de las optimizaciones JIT no traerá una mejora tan sorprendente.

Otra ventaja de los ECI es que JIT funciona montajes transversales, por lo que tiene más información si se quiere hacer procesos en línea.

Déjame explicar: digamos que tienes una clase base A y tienes solo una implementación es decir B en otro paquete/ensamblado/gema/etc. y se carga dinámicamente

El JIT ya que ver que B es la única aplicación de A, se puede sustituir por todas partes en su representación interna del A llama a los códigos B, y las llamadas a métodos no va a hacer un despacho (busque en vtable), pero serán llamadas directas. Esas llamadas directas pueden estar incluidas también. Por ejemplo, esta B tiene un método: getLength() que devuelve 2, todas las llamadas de getLength() pueden reducirse a constantes 2 en todas partes. Al final, un código C++ no podrá omitir la llamada virtual de B desde otro dll.

Algunas implementaciones de C++ no son compatibles con la optimización de más archivos .cpp (incluso hoy en día existe el indicador -lto en las versiones recientes de GCC que lo hace posible). Pero si usted es un desarrollador de C++ preocupado por la velocidad, probablemente coloque todas las clases sensibles en la misma biblioteca estática o incluso en el mismo archivo, para que el compilador pueda alinearlo bien, haciendo la información adicional que JIT tiene por diseño , que debe ser proporcionado por el desarrollador, por lo que no hay pérdida de rendimiento.

Cuestiones relacionadas