Clojure和ClojureScript哪个更快(为什么)?

19
如果我要猜的话,我相当确定答案是Clojure,但我不确定为什么。在逻辑上(对我来说),ClojureScript似乎应该更快:
两者都是“动态”的,但ClojureScript:
  • 编译成JavaScript,在V8引擎上运行
  • V8引擎可以说是最快的动态语言引擎
  • V8是用C编写的
而Clojure:
  • 也是动态的
  • 在JVM中运行,JVM没有内置的动态支持,因此我认为JVM也必须像V8一样做一些事情,以实现动态支持
  • 而且Java比C慢
那么Clojure怎么可能比ClojureScript更快呢?当说JavaScript是动态的,而Clojure也是动态的时,“动态”是否有不同的含义?我漏看了什么?
当然,如果ClojureScript确实更快,那么上述推理是否正确?
我猜,Clojure编译成什么......至少是问题的一部分。我知道JVM部分不能只是一个普通解释器(否则ClojureScript会更快),但Clojure不能编译成常规字节码,因为JVM中没有“动态”。那么ClojureScript如何编译/执行,Clojure如何编译/执行,以及普通Java如何编译/执行之间的差异以及每个中所暗示的性能差异是什么?

6
“Java比C慢”这个说法在很多问题领域可能是正确的,但并不相关。适当的问题是“Java比V8慢吗”。V8不会将您的代码作为编译后的C代码运行,它是一个JavaScript解释器,恰好是用C编写的。 - Eric J.
@EricJ。那么我想这就是我的问题了,我看到V8解释Clojure生成的JavaScript,而不是解释Clojure的JVM。 V8将JS编译为机器代码并运行它。 JVM做了什么更快? - lobsterism
2
Clojure和Java一样编译成字节码。你为什么认为它不是呢? - Diego Basch
@DiegoBasch JVM中没有动态支持。那么它如何比我理解中最快的动态运行时V8更快地执行动态方法呢? - lobsterism
6
@lobsterism 请阅读此链接:http://clojure.org/compilation - Diego Basch
3个回答

33
实际上,V8是用C++编写的。然而,它基本上与JVM执行的操作相同,而JVM是用C编写的。V8 JIT编译JavaScript代码并执行JIT编译的代码。同样,JVM JIT编译(或热点编译)字节码(而不是Java),并执行生成的代码。
字节码不是静态的,就像Java一样。实际上它可以非常动态。另一方面,Java大部分是静态的,并且将Java与字节码混淆是不正确的。Java编译器将Java源代码转换为字节码,JVM执行字节码。有关更多信息,建议您查看John Rose的博客(example)。那里有很多好的信息。此外,请尝试查找Cliff Click的讲座(例如this one)。
同样,Clojure代码直接编译成字节码,JVM再使用该字节码进行相同的过程。编译Clojure通常在运行时完成,这不是最快的过程。同样,将Clojurescript翻译成Javascript也不快。V8将Javascript翻译为可执行形式显然非常快。Clojure可以预先编译成字节码,这可以消除许多启动开销。
正如您所说,说JVM解释字节码也不是很正确。17年前的1.0版本更多地这样做了!
传统上,有两种编译模式。第一种模式是JIT(即时)编译器。其中字节码直接翻译为机器码。Java的JIT编译执行速度快,它不会生成高度优化的代码。它运行得还好。
第二种模式称为热点编译器。热点编译器非常复杂。它以解释模式快速启动程序,并在程序运行时对其进行分析。当检测到热点(代码中频繁执行的地方)时,它将编译这些代码。虽然JIT编译器必须快,因为除非它已经进行了JIT,否则没有任何执行,但热点编译器可以花费额外时间来优化正在编译的代码。

此外,如果有必要并且可能的话,它还可以回头重新访问代码,并对其进行更多的优化。这是热点编译器开始击败编译的C/C++的时候。因为它具有代码的运行时知识,所以它可以承担静态C/C++编译器无法做到的优化。例如,它可以内联虚函数。

热点编译器还有一个特点,据我所知,其他环境都没有,那就是如果有必要,它还可以取消代码优化。例如,如果代码一直采用单个分支,并且已经被优化,但运行时条件发生变化,强制将代码转到另一个(未优化的)分支,导致性能突然变得很差。热点编译器可以取消该函数的优化,并重新开始分析,以找出如何使其运行更好的方法。

热点编译器的一个缺点是启动有点慢。Java 7 JVM 的一个变化是将JIT编译器和热点编译器合并。虽然这种模式是新的,而且不是默认的,但一旦它初始化,启动速度应该很快,然后它就可以开始JVM非常擅长的高级优化了。

干杯!


谢谢,我其实不知道JVM支持动态。但是通过谷歌搜索,我偶然发现了http://blog.fogus.me/2011/10/14/why-clojure-doesnt-need-invokedynamic-but-it-might-be-nice,这篇文章展示了Clojure实际上并没有利用该特性,但也帮助我理解了为什么它仍然可以快速运行。 - lobsterism
如果你观看了Rich Hickey去年的Clojure Conj主题演讲,他谈到了对该功能的支持。这是很好的,但旧版JVM可能会出现问题。 - BillRobertson42
1
还有,我忘记回答你的问题了。这取决于情况。;-) - BillRobertson42

28

这个问题很难精确回答,没有具体的基准任务(甚至特定版本的Clojure或ClojureScript)作为参考。

话虽如此,在大多数情况下,我会期望Clojure要稍微快一些。原因如下:

  • Clojure通常编译成静态代码,因此实际上不会在运行时执行任何动态查找。这非常重要:高性能代码通常产生的字节码与静态类型的Java非常相似。该问题似乎做出了错误的假设,即动态语言必须在运行时进行动态方法查找:这并不总是正确的(通常在Clojure中不是这样)
  • JVM JIT非常良好的工程化,我认为它目前仍然比JavaScript JIT更好,尽管V8有多么优秀。
  • 如果需要并发性或需要利用多个核心,则显然没有竞争,因为JavaScript是单线程的.....
  • Clojure编译器比ClojureScript更成熟,并且近年来进行了许多性能调优工作(包括原始支持,协议等)

当然,在任何语言中都可以编写快速或缓慢的代码。这将比语言实现之间的根本差异更有影响。

而且,从根本上说,您在Clojure和ClojureScript之间的选择不应该是关于性能的任何情况。两者都提供引人注目的生产力优势。主要决定因素应该是:

  • 如果要在Web上运行,请使用ClojureScript
  • 如果要在JVM环境中的服务器上运行,请使用Clojure

1
好的,我认为你的第一个观点是我困惑的根源。所以你的意思是,如果有人在“滥用”动态内容,并且运行时一直进行动态查找,那么谁知道哪个更快,但这种情况99%的时间都不会发生,字节码在那里击败了解释代码,对吗?你有任何想法百分比的性能差异会是多少?我猜如果纯执行是瓶颈,那么它与JS vs Java相同的百分比差异? - lobsterism
3
Java和JavaScript的对比可能会给出优化良好的Clojure和ClojureScript之间近似%的差异。毕竟,各自的平台是限制因素.....长远来看,随着编译器变得越来越好,您可以期望Clojure趋向于Java性能,ClojureScript趋向于JavaScript性能。 - mikera

2
这不仅仅是一个答案,而是一条历史评论:HotSpot VM和V8 js引擎都可以追溯到Sun Microsystems的Self项目,我认为该项目原型化了许多使它们以如此快的速度运行的技术。在比较它们时需要考虑这些内容。我本想将其发布为评论,但声誉系统却阻止了我。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接