是否有适合编写脚本的快速启动Haskell解释器?

时间:2009-04-03 05:55:26

标签: performance scripting haskell interpreter

有没有人知道一个快速启动的Haskell解释器,它适合用于编写shell脚本?使用Hugs运行'hello world'在我的旧笔记本电脑上运行了400ms,在我目前的Thinkpad X300上运行了300ms。这对于瞬时响应来说太慢了。与GHCi的时代相似。

功能语言不一定要慢:Objective Caml和Moscow ML在1ms或更短时间内运行hello world。

澄清:我是GHC的重要用户,我知道如何使用GHCi。我知道所有关于编译以使事情变得快速的事情。解析成本应该完全不相关:如果ML和OCaml的启动速度比GHCi快300倍,那么还有改进的余地。

我正在寻找

  • 脚本的便利性:一个源文件,没有二进制代码,相同的代码在所有平台上运行
  • 与其他解释器相媲美的性能,包括

    等简单程序的快速启动和执行
    module Main where
    main = print 33
    

正在为更严肃的程序寻找编译性能。重点是看看Haskell是否对脚本有用。

5 个答案:

答案 0 :(得分:31)

答案 1 :(得分:8)

使用ghc -e几乎等同于调用ghci。我相信GHC的runhaskell在运行之前将代码编译为临时可执行文件,而不是像ghc -e / ghci那样解释它,但我不是100%肯定。

$ 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

在运行之前简单地编译所有“脚本”有多难?

修改

啊,为多种架构提供二进制文件确实很痛苦。我之前走过那条路,这并不是很有趣......

可悲的是,我认为不可能更好地使任何Haskell编译器的启动开销。语言的声明性意味着,在尝试进行类型检查之前,有必要首先阅读整个程序,从不执行任何操作,然后您要么承受严格性分析或不必要的懒惰和thunking的成本。

流行的'脚本'语言(shell,Perl,Python等)和基于ML的语言只需要一次通过......好吧,ML需要静态类型检查传递,而Perl有这个有趣的5通道方案(其中两个反向运行);无论哪种方式,程序化都意味着编译器/解释器可以更容易地将程序的各个部分组合在一起。

简而言之,我认为不可能比这更好。我没有测试过Hugs或GHCi是否有更快的启动,但是任何差异仍然远离非Haskell语言。

答案 2 :(得分:5)

如果您真的关心速度,那么每次启动时重新解析代码都会受到阻碍。 Haskell不需要从解释器运行,用GHC编译它就可以获得出色的性能。

答案 3 :(得分:4)

这个问题分为两部分:

  • 你关心表现
  • 您想要编写脚本

如果你关心表现,唯一严肃的选择是GHC,这是非常快的:http://shootout.alioth.debian.org/u64q/benchmark.php?test=all&lang=all

如果你想为Unix脚本编写一些简单的东西,我会使用GHCi。它比Hugs快约30倍,但也支持所有hackage库。

现在安装GHC(并免费获得GHCi)。

答案 4 :(得分:3)

如果有一个ghci守护进程和一个接受脚本路径和位置的馈送脚本,与已经运行的ghci进程通信以在正确的目录中加载和执行脚本并将输出传递回stdout的feeder脚本?

不幸的是,我不知道如何写这样的东西,但似乎它可以通过以下的速度来判断:l在ghci中。因为看起来runhaskell的大部分成本都是在启动ghci,而不是解析和运行脚本。

编辑:经过一番游戏,我发现Hint package(GHC API的包装)在这里非常有用。以下代码将加载传入的模块名称(此处假定位于同一目录中)并将执行main函数。现在'所有'剩下的就是让它成为一个守护进程,让它接受管道或套接字上的脚本。

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:就stdout / err / in go而言,使用this specific GHC trick看起来可以将客户端程序的标准重定向到进纸器程序,然后进入一些命名管道(可能),守护程序始终连接到该管道,然后将守护程序的stdout返回到进程程序正在侦听的另一个命名管道。伪示例:

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

这里的馈线是一个小编译的线束程序,只需将std的重定向到守护进程中,然后从守护进程中退出,并将该脚本的名称和位置提供给守护进程。