我有一个ClojureScript程序,主要对集合执行数学计算。它是在惯用的,独立于主机的Clojure中开发的,因此很容易对它进行基准测试。令我惊讶的是(与Which is faster, Clojure or ClojureScript (and why)?的答案相反),ClojureScript中的相同代码运行速度比Clojure等同的速度低5-10倍。
这就是我所做的。我在http://clojurescript.net/打开了lein repl
和浏览器代表。然后我在两个REPL中尝试了这些片段。
(time (dotimes [x 1000000] (+ 2 8)))
(let [coll (list 1 2 3)] (time (dotimes [x 1000000] (first coll))))
然后我在浏览器repl上打开了一个javascript控制台并编写了一个极简主义基准函数,
function benchmark(count, fun) {
var t0 = new Date();
for (i = 0; i < count; i++) {
fun();
}
var t1 = new Date();
return t1.getTime() - t0.getTime();
}
返回浏览器REPL:
(defn multiply [] (* 42 1.2))
然后在javascript控制台中尝试本机javascript乘法及其clojurescript变体,
benchmark(1000000, cljs.user.multiply);
benchmark(1000000, function(){ 42 * 1.2 });
我找到了什么
现在我的问题是,如何改善ClojureScript程序的性能?
到目前为止,我已经考虑了一些方法
(aget js/v 0)
答案 0 :(得分:10)
JavaScript有明确的回报,所以
function () { 42 * 1.2 }
什么都不做;你想要进行基准测试
function () { return 42 * 1.2 }
代替。这恰好是ClojureScript版本编译的内容,因此没有任何区别(在ClojureScript中,非高阶用法中的基本算术函数被内联为常规的基于运算符的JavaScript表达式)。
现在,Clojure在这一点上肯定比ClojureScript更快。部分原因是Clojure仍然比ClojureScript更精心调整,尽管ClojureScript在这个部门的发展速度非常快。另一部分是Clojure有一个更成熟的JIT可以利用(现代的JS引擎,尤其是V8,非常棒,但还不是HotSpot级)。
但差异的大小在某种程度上难以衡量;涉及JIT的事实意味着一个没有任何副作用的主体的循环,例如问题中的那个,可能会被优化掉,甚至可能在第一次运行时(通过使用堆栈替换) ,由HotSpot和使用我认为也是V8 - 我必须检查以确定)。所以,最好像
这样的基准测试(def arr (long-array 1))
;;; benchmark this
(dotimes [_ 1000000]
(aset (longs arr) 0 (inc (aget (longs arr) 0))))
(longs
要求避免在Clojure中反思;也可以使用^longs
提示。
最后,在Clojure和ClojureScript中肯定是这样的,对于某些特别对性能敏感的代码,最好使用本机数组等。令人高兴的是,这样做没有问题:在ClojureScript方面,你有array
,js-obj
,aget
,aset
,make-array
,你可以在:mutable
中的字段上使用deftype
元数据,以便能够在方法体等中set!
。
答案 1 :(得分:7)
ClojureScript math 是 JavaScript数学。是的,如果性能至关重要,请使用JavaScript数组和提供的低级运算符,这些运算符可以保证在可能的情况下生成最佳代码(即没有更高的订单使用率)。 ClojureScript持久性数据结构以这种方式编写:数组变异,算术,比特翻转。
我有一个高效的ClojureScript示例 - http://github.com/swannodette/cljs-stl/blob/master/src/cljs_stl/spectral/demo.cljs,您可能会觉得它很有用。