与Java和Python相比,为什么Clojure Hello World程序如此之慢?

时间:2010-03-28 01:49:23

标签: performance functional-programming comparison jvm clojure

更新

正如许多人所建议的,看起来这是因为clojure代码首先被编译然后执行。 AOT编译应该有助于抵消这一点。鉴于我发现实际的Clojure AOT编译过程有点难以解决(类路径问题,目录问题等),我已经逐步编写了一个小步骤here,以防有人感兴趣。


大家好,

我正在阅读“编程Clojure”,我正在比较一些我用于一些简单代码的语言。我注意到clojure实现在每种情况下都是最慢的。例如,

Python - hello.py

def hello_world(name):
  print "Hello, %s" % name

hello_world("world")

和结果,

$ time python hello.py
Hello, world

real    0m0.027s
user    0m0.013s
sys 0m0.014s

Java - hello.java

import java.io.*;

public class hello {
  public static void hello_world(String name) {
    System.out.println("Hello, " + name);
  }

  public static void main(String[] args) {
    hello_world("world");
  }
}

和结果,

$ time java hello
Hello, world

real    0m0.324s
user    0m0.296s
sys 0m0.065s

最后,

Clojure - hellofun.clj

(defn hello-world [username]
  (println (format "Hello, %s" username)))

(hello-world "world")

和结果,

$ time clj hellofun.clj 
Hello, world

real    0m1.418s
user    0m1.649s
sys 0m0.154s

这是一个整体,garangutan 1.4秒!

有没有人指出这可能是什么原因? Clojure真的那么慢,还是需要使用JVM技巧等来加速执行?

更重要的是 - 在某种程度上,性能上的巨大差异是不是会成为一个问题? (我的意思是,假设我在生产系统中使用Clojure - 使用lisp获得的收益似乎完全被我在这里看到的性能问题所抵消)。


这里使用的机器是运行Snow Leopard的2007 Macbook Pro,2.16Ghz Intel C2D和2G DDR2 SDRAM。

BTW,我使用的clj脚本来自here,看起来像,

#!/bin/bash
JAVA=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java
CLJ_DIR=/opt/jars
CLOJURE=$CLJ_DIR/clojure.jar
CONTRIB=$CLJ_DIR/clojure-contrib.jar
JLINE=$CLJ_DIR/jline-0.9.94.jar
CP=$PWD:$CLOJURE:$JLINE:$CONTRIB

# Add extra jars as specified by `.clojure` file
if [ -f .clojure ]
then
  CP=$CP:`cat .clojure`
fi

if [ -z "$1" ]; then
  $JAVA -server -cp $CP \
      jline.ConsoleRunner clojure.lang.Repl
else
  scriptname=$1
  $JAVA -server -cp $CP clojure.main $scriptname -- $*
fi

4 个答案:

答案 0 :(得分:27)

除了Clojure启动时间,你在这里测量的不多。您也正在以一种测量编译时间的方式运行程序。如果您希望看到更快的加载时间,则需要提前编译代码。

在Python中编写了一些代码,我发现Clojure作为一般规则比Python更快,更快,你通常可以得到一个Clojure程序,以获得纯Java速度的2X-4X。

答案 1 :(得分:2)

要添加到dnolen的答案,在执行Python与Clojure计时内容时,您可能希望将“基本工作单元”打包为函数,然后使用time宏(在Clojure中)或{{ 1}}函数(在Python中;或者,更好的是,使用IPython的计时功能)在REPL。结果应该大致相当。 (请注意,Clojure代码需要通过运行几次才能“预热”以实现完全性能。)

还有一些针对Clojure的基准测试套件,例如Criterium;你可能想考虑使用其中一个。

答案 2 :(得分:2)

另请注意,clj脚本中的“-server”选项将使用“服务器JVM”,该服务器JVM针对长时间运行的进程进行了优化,代价是启动时间较慢。

您的java示例未包含此选项,因此可能正在使用“客户端JVM”,该客户端JVM针对更快的启动时间进行了优化。

尝试运行java -jar clojure.jar -i hellofun.clj以进行更公平的比较。

答案 3 :(得分:1)

与本机语言或解释语言相比,JVM通常具有稍慢的启动时间。最重要的是,Clojure为启动时间增加了相当大的开销,因为它在启动时编译并加载了相当多的代码。即使使用AOT,Clojure在运行之前还需要设置很多东西。

最重要的是,不要依赖Clojure进行短期流程。在大多数情况下,甚至不要依赖Java来处理这些用例。像Node.js,Python,Lua等原生或解释的东西会好得多。

对于中长期流程,Clojure平均比几乎所有其他动态语言快得多,击败了Python和Ruby。如果需要,Clojure可以像Java一样快速地完成,而且它的Java交互操作非常简单,通过将一些函数更改为纯java,您可以在大多数情况下获得与Java相等的速度。

现在,如果您真的想要Clojure的快速内容,我建议您查看lumo。它是一个自包含并在自举ClojureScript上运行的ClojureScript REPL,因此无法看到JVM。

time python -c "print(\"Hello World\")"
Hello World

real    0m0.266s
user    0m0.015s
sys     0m0.202s

time lumo -e "\"Hello World\""
"Hello World"

real    0m0.438s
user    0m0.000s
sys     0m0.203s

正如您所看到的,Lumo非常接近Cpy3k的启动速度。

一个替代方案,它不再是Clojure了,但它仍然是一个受Clojure启发的Lisp,是Hy。它是一个在Python上运行Clojure语法的Lisp。

time hy -c "(print \"Hello World\")"
Hello World

real    0m0.902s
user    0m0.000s
sys     0m0.171s

它的启动时间比Cpy3k和Lumo稍慢,但它可以为您提供所有Python,并使用Clojure的语法和宏。