secuencias secuencia google funcion encuentra comandos performance scripting haskell interpreter

performance - funcion - editor de secuencias de comandos google



¿Hay un intérprete de Haskell de inicio rápido adecuado para las secuencias de comandos? (5)

¿Por qué no crear un front-end de script que compile el script si no ha estado antes o si la versión compilada está desactualizada?

Aquí está la idea básica, este código podría mejorarse mucho: busque la ruta en lugar de asumir que todo está en el mismo directorio, maneje mejor otras extensiones de archivo, etc. También estoy bastante verde en la codificación de haskell (ghc-compiled-script. hs):

import Control.Monad import System import System.Directory import System.IO import System.Posix.Files import System.Posix.Process import System.Process getMTime f = getFileStatus f >>= return . modificationTime main = do scr : args <- getArgs let cscr = takeWhile (/= ''.'') scr scrExists <- doesFileExist scr cscrExists <- doesFileExist cscr compile <- if scrExists && cscrExists then do scrMTime <- getMTime scr cscrMTime <- getMTime cscr return $ cscrMTime <= scrMTime else return True when compile $ do r <- system $ "ghc --make " ++ scr case r of ExitFailure i -> do hPutStrLn stderr $ "''ghc --make " ++ scr ++ "'' failed: " ++ show i exitFailure ExitSuccess -> return () executeFile cscr False args Nothing

Ahora podemos crear scripts como este (hs-echo.hs):

#! ghc-compiled-script import Data.List import System import System.Environment main = do args <- getArgs putStrLn $ foldl (++) "" $ intersperse " " args

Y ahora ejecutándolo:

$ time hs-echo.hs "Hello, world/!" [1 of 1] Compiling Main ( hs-echo.hs, hs-echo.o ) Linking hs-echo ... Hello, world! hs-echo.hs "Hello, world!" 0.83s user 0.21s system 97% cpu 1.062 total $ time hs-echo.hs "Hello, world, again/!" Hello, world, again! hs-echo.hs "Hello, world, again!" 0.01s user 0.00s system 60% cpu 0.022 total

¿Alguien sabe de un intérprete de Haskell de inicio rápido que sea adecuado para escribir scripts de shell? Ejecutar ''hola mundo'' usando Hugs tomó 400 ms en mi vieja computadora portátil y tomó 300 ms en mi Thinkpad X300 actual. Eso es demasiado lento para la respuesta instantánea. Los tiempos con GHCi son similares.

Los lenguajes funcionales no tienen que ser lentos: tanto Objective Caml como Moscow ML ejecutan hola mundo en 1 ms o menos.

Aclaración : soy un gran usuario de GHC y sé cómo usar GHCi. Sé todo sobre compilar para conseguir cosas rápido. Los costos del análisis deben ser completamente irrelevantes: si ML y OCaml pueden comenzar 300 veces más rápido que GHCi, entonces hay espacio para mejorar.

Busco

  • La comodidad de las secuencias de comandos: un archivo de origen, sin código binario, el mismo código se ejecuta en todas las plataformas
  • Rendimiento comparable al de otros intérpretes, incluido el inicio y la ejecución rápidos para un programa simple como

    module Main where main = print 33

No estoy buscando un rendimiento compilado para programas más serios. El punto principal es ver si Haskell puede ser útil para las secuencias de comandos.


¿Qué pasa con tener un demonio ghci y un script de feeder que toma la ruta y la ubicación del script, se comunica con el proceso ghci que ya se está ejecutando para cargar y ejecutar el script en el directorio adecuado y canaliza la salida al script de feeds para stdout?

Desafortunadamente, no tengo idea de cómo escribir algo como esto, pero parece que podría ser realmente rápido a juzgar por la velocidad de: l en ghci. Como parece, la mayor parte del costo en runhaskell es iniciar Ghci, no analizar y ejecutar el script.

Edición: Después de jugar un poco, encontré que el paquete Hint (una envoltura alrededor de la API de GHC) es de perfecto uso aquí. El siguiente código cargará el nombre del módulo pasado (aquí se supone que está en el mismo directorio) y ejecutará la función principal. Ahora ''todo'' lo que queda es convertirlo en un demonio, y hacer que acepte scripts en una tubería o socket.

import Language.Haskell.Interpreter import Control.Monad run = runInterpreter . test test :: String -> Interpreter () test mname = do loadModules [mname ++ ".hs"] setTopLevelModules [mname] res <- interpret "main" (as :: IO()) liftIO res

Edit2: En lo que respecta a stdout / err / in, usando este truco específico de GHC Parece que sería posible redireccionar los std del programa cliente al programa alimentador, luego a algunas canalizaciones con nombre (quizás) que el demonio está conectado en todo momento, y luego devolver la salida estándar del daemon a otra canalización con nombre que el programa de alimentación está escuchando. Pseudo-ejemplo:

grep ... | feeder my_script.hs | xargs ... | ^---------------- < V | named pipe -> daemon -> named pipe

Aquí, el alimentador sería un pequeño programa de arnés compilado para simplemente redirigir los std hacia y luego salir del daemon y dar el nombre y la ubicación de la secuencia de comandos al daemon.


Si está realmente preocupado por la velocidad, se verá obstaculizado por volver a analizar el código de cada lanzamiento. Haskell no necesita ejecutarse desde un intérprete, compilarlo con GHC y debería obtener un rendimiento excelente.


Tienes dos partes para esta pregunta:

  • te importa el rendimiento
  • quieres scripting

Si te importa el rendimiento, la única opción seria es GHC, que es muy rápida: http://shootout.alioth.debian.org/u64q/benchmark.php?test=all&lang=all

Si quieres algo liviano para los scripts de Unix, usaría GHCi. Es aproximadamente 30 veces más rápido que Hugs, pero también es compatible con todas las bibliotecas en hackage.

Entonces instale GHC ahora (y obtenga GHCi gratis).


Usar ghc -e es prácticamente equivalente a invocar ghci . Creo que runhaskell de GHC compila el código en un ejecutable temporal antes de ejecutarlo, en lugar de interpretarlo como ghc -e / ghci , pero no estoy 100% seguro.

$ time echo ''Hello, world!'' Hello, world! real 0m0.021s user 0m0.000s sys 0m0.000s $ time ghc -e ''putStrLn "Hello, world!"'' Hello, world! real 0m0.401s user 0m0.031s sys 0m0.015s $ echo ''main = putStrLn "Hello, world!"'' > hw.hs $ time runhaskell hw.hs Hello, world! real 0m0.335s user 0m0.015s sys 0m0.015s $ time ghc --make hw [1 of 1] Compiling Main ( hw.hs, hw.o ) Linking hw ... real 0m0.855s user 0m0.015s sys 0m0.015s $ time ./hw Hello, world! real 0m0.037s user 0m0.015s sys 0m0.000s

¿Qué tan difícil es simplemente compilar todos sus "scripts" antes de ejecutarlos?

Editar

Ah, proporcionar binarios para múltiples arquitecturas es realmente un dolor. He ido por ese camino antes, y no es muy divertido ...

Lamentablemente, no creo que sea posible mejorar la sobrecarga de inicio de cualquier compilador de Haskell. La naturaleza declarativa del lenguaje significa que es necesario leer todo el programa primero, incluso antes de tratar de verificar cualquier cosa, sin importarle la ejecución, y luego, o bien sufres el costo del análisis de rigor o la pereza y el ruido innecesarios.

Los lenguajes populares de ''scripting'' (shell, Perl, Python, etc.) y los lenguajes basados ​​en ML solo requieren un solo paso ... bueno, ML requiere un pase de comprobación de tipo estático y Perl tiene este divertido esquema de 5 pases (con dos de ellos corriendo en reversa); de cualquier manera, ser procedimental significa que el compilador / intérprete tiene mucho más fácil un trabajo de ensamblar los bits del programa juntos.

En resumen, no creo que sea posible mejorar mucho más que esto. No he probado para ver si Hugs o GHCi tiene un inicio más rápido, pero cualquier diferencia todavía está lejos de los idiomas que no son de Haskell.