Tengo un pequeño marco de prueba. Ejecuta un ciclo que hace lo siguiente:Acelerar runhaskell
Genera un pequeño archivo fuente Haskell.
Ejecute esto con
runhaskell
. El programa genera varios archivos de disco.Procese los archivos de disco recién generados.
Esto ocurre varias docenas de veces. Resulta que runhaskell
ocupa la gran mayoría del tiempo de ejecución del programa.
Por un lado, el hecho de que runhaskell
logra cargar un archivo del disco, convertirlo en token, analizarlo, hacer un análisis de dependencia, cargar 20KB más de texto del disco, ensamblar y analizar todo esto, realizar inferencia de tipo completo, verificar tipos, desugar a Core, enlazar contra código de máquina compilado, y ejecutarlo en un intérprete, todo dentro de 2 segundos de tiempo de pared, en realidad es bastante impresionante cuando lo piensas. Por otro lado, todavía quiero que sea más rápido. ;-)
La compilación del probador (el programa que ejecuta el ciclo anterior) produjo una pequeña diferencia de rendimiento. La compilación de los 20 KB del código de biblioteca con el que se vinculan los scripts produjo una mejora bastante más notable. Pero aún tarda aproximadamente 1 segundo por invocación de runhaskell
.
Los archivos Haskell generados tienen poco más de 1 KB cada uno, pero solo una parte del archivo realmente cambia. Quizás compilar el archivo y usar el interruptor -e
de GHC sería más rápido?
Alternativamente, ¿tal vez sea la sobrecarga de crear y destruir repetidamente muchos procesos del sistema operativo, lo que está ralentizando esto? Cada invocación de runhaskell
presuntamente hace que el sistema operativo explore la ruta de búsqueda del sistema, localice el archivo binario necesario, lo cargue en la memoria (seguramente esto ya está en la memoria caché de disco), lo conecte con cualquier DLL y lo encienda. ¿Hay alguna forma en que pueda (fácilmente) mantener en ejecución una instancia de GHC, en lugar de tener que crear y destruir constantemente el proceso del sistema operativo?
En última instancia, supongo que siempre existe la API de GHC. Pero, como yo lo entiendo, eso es terriblemente difícil de usar, altamente indocumentado y propenso a cambios radicales en cada lanzamiento de punto menor de GHC. La tarea que intento realizar es muy simple, así que no quiero hacer las cosas más complejas de lo necesario.
Sugerencias?
Actualización: Cambio a GHC -e
(es decir, ahora todo se compila excepto la frase siendo ejecutado) no hizo ninguna diferencia de rendimiento medible. Parece bastante claro en este punto que todo es sobrecarga del sistema operativo. Me pregunto si podría quizás crear una tubería del probador a GHCi y así hacer uso de un solo proceso del sistema operativo ...
Todo su flujo de trabajo no se ve exactamente orientado al rendimiento, ¿o sí? ¿Por qué tienes que crear el código Haskell? – leftaroundabout
¡Obviamente necesita un daemon de GHC! : p (algunas personas que conozco solían bromear sobre la creación de un daemon grep para evitar la sobrecarga de llamar continuamente a grep durante el arranque, etc.) – ivanm
+1 para un intento de optimización justificado y bien ejecutado. – delnan