8

我有一个 ClojureScript 程序,主要对集合执行数学计算。它是在惯用的、独立于主机的 Clojure 中开发的,因此很容易对其进行基准测试。令我惊讶的是(与答案所暗示的哪个更快,Clojure 还是 ClojureScript(以及为什么)?),ClojureScript 中的相同代码的运行速度比其 Clojure 等效代码慢 5-10 倍。

这就是我所做的。我在http://clojurescript.net/lein repl打开了一个浏览器 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 });

我发现了什么

  • 本机 javascript 数学与 clojure 中的数学相当
  • ClojureScript 比其中任何一个都慢 5-10 倍

现在我的问题是,如何提高 ClojureScript 程序的性能?

到目前为止我已经考虑了一些方法

  • 回退到在幕后使用可变的 JavaScript 数组和对象。(这可能吗?)
  • 回退到使用原生 JavaScript 数学运算符。(这可能吗?)
  • 显式使用 javascript 数组(aget js/v 0)
  • 使用不那么雄心勃勃的 clojure-for-javascript 实现,例如https://github.com/chlorinejs/chlorinehttps://github.com/gozala/wisp它们生成更惯用的 javascript,但它们不支持命名空间我经常使用它。
4

2 回答 2

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!在方法主体等中使用它们。

于 2013-05-14T19:59:00.807 回答
7

ClojureScript 数学JavaScript 数学。是的,如果性能至关重要,请使用 JavaScript 数组和提供的低级运算符,这些可以保证在可能的情况下生成最佳代码(即没有更高阶的使用)。ClojureScript 持久数据结构是这样编写的:数组变异、算术、位旋转。

我有一个高效 ClojureScript 的小例子 - http://github.com/swannodette/cljs-stl/blob/master/src/cljs_stl/spectral/demo.cljs,您可能会发现它可以作为指导。

于 2013-05-14T20:35:10.203 回答