Javascript 如何提高ClojureScript性能
我最近开始使用ClojureScript。当我将JavaScript程序重写为ClojureScript时,我担心ClojureScript的性能 ClojureScript代码Javascript 如何提高ClojureScript性能,javascript,clojure,clojurescript,Javascript,Clojure,Clojurescript,我最近开始使用ClojureScript。当我将JavaScript程序重写为ClojureScript时,我担心ClojureScript的性能 ClojureScript代码 (def NUM 10000) (def data (vec (repeatedly NUM #(hash-map :x (rand) :y (rand))))) (.time js/console "cljs") (loop [x 0 y 0 d data] (if (empty? d) [x y]
(def NUM 10000)
(def data
(vec (repeatedly NUM #(hash-map :x (rand) :y (rand)))))
(.time js/console "cljs")
(loop [x 0 y 0 d data]
(if (empty? d)
[x y]
(recur (+ x (:x (first d)))
(+ y (:y (first d)))
(rest d))))
(.timeEnd js/console "cljs")
benchmark_cljs.benchmark.NUM = 1E4;
benchmark_cljs.benchmark.data = cljs.core.vec.call(null, cljs.core.repeatedly.call(null, benchmark_cljs.benchmark.NUM, function() {
return cljs.core.PersistentHashMap.fromArrays.call(null, [new cljs.core.Keyword(null, "x", "x", 1013904362), new cljs.core.Keyword(null , "y", "y", 1013904363)], [cljs.core.rand.call(null), cljs.core.rand.call(null)]);
}));
console.time("cljs");
var x_4753 = 0;
var y_4754 = 0;
var d_4755 = benchmark_cljs.benchmark.data;
while (true) {
if (cljs.core.empty_QMARK_.call(null, d_4755)) {
new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [x_4753, y_4754], null);
} else {
var G__4756 = x_4753 + (new cljs.core.Keyword(null, "x", "x", 1013904362)).cljs$core$IFn$_invoke$arity$1(cljs.core.first.call(null, d _4755));
var G__4757 = y_4754 + (new cljs.core.Keyword(null, "y", "y", 1013904363)).cljs$core$IFn$_invoke$arity$1(cljs.core.first.call(null, d _4755));
var G__4758 = cljs.core.rest.call(null, d_4755);
x_4753 = G__4756;
y_4754 = G__4757;
d_4755 = G__4758;
continue;
}
break;
}
console.timeEnd("cljs");
var NUM = 10000;
var data = [];
for (var i = 0; i < NUM; i++) {
data[i] = {
x: Math.random(),
y: Math.random()
}
}
console.time('js');
var x = 0;
var y = 0;
for (var i = 0; i < data.length; i++) {
x += data[i].x;
y += data[i].y;
}
console.timeEnd('js');
ClojureScript(optimizations :whitespace): 30 〜 70ms
ClojureScript(optimizations :advanced): 9 〜 13ms
JavaScript: 0.3ms 〜 0.9ms
编译的JavaScript代码(优化:空白)
JavaScript代码
(def NUM 10000)
(def data
(vec (repeatedly NUM #(hash-map :x (rand) :y (rand)))))
(.time js/console "cljs")
(loop [x 0 y 0 d data]
(if (empty? d)
[x y]
(recur (+ x (:x (first d)))
(+ y (:y (first d)))
(rest d))))
(.timeEnd js/console "cljs")
benchmark_cljs.benchmark.NUM = 1E4;
benchmark_cljs.benchmark.data = cljs.core.vec.call(null, cljs.core.repeatedly.call(null, benchmark_cljs.benchmark.NUM, function() {
return cljs.core.PersistentHashMap.fromArrays.call(null, [new cljs.core.Keyword(null, "x", "x", 1013904362), new cljs.core.Keyword(null , "y", "y", 1013904363)], [cljs.core.rand.call(null), cljs.core.rand.call(null)]);
}));
console.time("cljs");
var x_4753 = 0;
var y_4754 = 0;
var d_4755 = benchmark_cljs.benchmark.data;
while (true) {
if (cljs.core.empty_QMARK_.call(null, d_4755)) {
new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [x_4753, y_4754], null);
} else {
var G__4756 = x_4753 + (new cljs.core.Keyword(null, "x", "x", 1013904362)).cljs$core$IFn$_invoke$arity$1(cljs.core.first.call(null, d _4755));
var G__4757 = y_4754 + (new cljs.core.Keyword(null, "y", "y", 1013904363)).cljs$core$IFn$_invoke$arity$1(cljs.core.first.call(null, d _4755));
var G__4758 = cljs.core.rest.call(null, d_4755);
x_4753 = G__4756;
y_4754 = G__4757;
d_4755 = G__4758;
continue;
}
break;
}
console.timeEnd("cljs");
var NUM = 10000;
var data = [];
for (var i = 0; i < NUM; i++) {
data[i] = {
x: Math.random(),
y: Math.random()
}
}
console.time('js');
var x = 0;
var y = 0;
for (var i = 0; i < data.length; i++) {
x += data[i].x;
y += data[i].y;
}
console.timeEnd('js');
ClojureScript(optimizations :whitespace): 30 〜 70ms
ClojureScript(optimizations :advanced): 9 〜 13ms
JavaScript: 0.3ms 〜 0.9ms
请告诉我如何提高ClojureScript的处理时间
提前感谢。您在ClojureScript中使用持久数据结构,在JavaScript中使用可变数组和对象。预计这两个代码段的性能特征会有所不同 现在,如果性能对您所做的事情非常关键,而持久性没有任何好处,那么您可以使用ClojureScript中的数组和对象:
(def NUM 10000)
(def data (array))
(loop [i 0]
(when (< i NUM)
(aset data i (js-obj "x" (js/Math.random) "y" (js/Math.random)))
(recur (inc i))))
(let [lim (alength data)]
(loop [x 0 y 0 i 0]
(if (< i lim)
(recur (+ x (aget data i "x"))
(+ y (aget data i "y"))
(inc i))
(println x y))))
(def NUM 10000)
(def数据(阵列))
(循环[i0]
(当(
另一方面,如果您确实需要保留所涉及的数据结构的旧版本,您可能会通过不必制作完整的副本来保存它们来赢回“失去的时间”。根据您需要的性能和您愿意放弃的内容,您有许多选择。如果你感兴趣的话,我在GitHub上放了一些 通过使用记录和本机字段访问,您可以将原始ClojureScript解决方案的运行时减少一半:
(定义记录XY[x y])
(def数据(mapv(fn[u](XY.(rand)(rand)))(range NUM)))
(定义sumXsAndYsWithLoopAndNativeFieldAccess[数据]
(循环[x0y0数据]
(如有)(序号数据)
(让[o(第一数据)]
(重复(+x(-x o))(+y(-y o))(剩余数据)))
[x y]))
(时间(sumXsAndYsWithLoopAndNativeFieldAccess数据))
您还可以将数组用作可变的局部变量,并且获得的解决方案的速度仅为本机JavaScript版本的8倍:
(defn sumsXsAndYsWithDotimesNativeFieldAccessAndMutableLocals [data]
(let [x (doto (make-array 1)
(aset 0 0))
y (doto (make-array 1)
(aset 0 0))]
(dotimes [i (count data)]
(let [o (data i)]
(aset x 0 (+ (aget x 0) (.-x o)))
(aset y 0 (+ (aget y 0) (.-y o)))))
[(aget x 0) (aget y 0)]))
(time (sumsXsAndYsWithDotimesNativeFieldAccessAndMutableLocals data))
(def data (into-array (mapv #(XY. (rand) (rand)) (range NUM))))
(defn sumsXsAndYsWithDotimesOnArrayNativeFieldAccessAndMutableLocals [data]
(let [x (doto (make-array 1)
(aset 0 0))
y (doto (make-array 1)
(aset 0 0))]
(dotimes [i (alength data)]
(let [o (aget data i)]
(aset x 0 (+ (aget x 0) (.-x o)))
(aset y 0 (+ (aget y 0) (.-y o)))))
[(aget x 0) (aget y 0)]))
(time (sumsXsAndYsWithDotimesOnArrayNativeFieldAccessAndMutableLocals data))
此外,您可以将上述方法与阵列结合使用,从而实现一个速度大约是本机JavaScript版本速度3倍的解决方案:
(defn sumsXsAndYsWithDotimesNativeFieldAccessAndMutableLocals [data]
(let [x (doto (make-array 1)
(aset 0 0))
y (doto (make-array 1)
(aset 0 0))]
(dotimes [i (count data)]
(let [o (data i)]
(aset x 0 (+ (aget x 0) (.-x o)))
(aset y 0 (+ (aget y 0) (.-y o)))))
[(aget x 0) (aget y 0)]))
(time (sumsXsAndYsWithDotimesNativeFieldAccessAndMutableLocals data))
(def data (into-array (mapv #(XY. (rand) (rand)) (range NUM))))
(defn sumsXsAndYsWithDotimesOnArrayNativeFieldAccessAndMutableLocals [data]
(let [x (doto (make-array 1)
(aset 0 0))
y (doto (make-array 1)
(aset 0 0))]
(dotimes [i (alength data)]
(let [o (aget data i)]
(aset x 0 (+ (aget x 0) (.-x o)))
(aset y 0 (+ (aget y 0) (.-y o)))))
[(aget x 0) (aget y 0)]))
(time (sumsXsAndYsWithDotimesOnArrayNativeFieldAccessAndMutableLocals data))
你可能想看看大卫·诺伦的项目。他有一些很好的宏,用于创建和更新本地可变项,使上述内容看起来不那么可笑
无论如何,希望这能有所帮助。你能发布ClojureScript生成的已编译JavaScript输出吗?我添加了已编译的JavaScript代码。由于Clojure本机数据结构是不可变的,所以说JS和ClJS在做相同的事情是错误的。这是两种不同的语言,即使ClJS被编译成JS。我在想,也许一个用于理解的
可以很好地编译成JavaScript,比如:(def data(用于[x(range NUM)]{:x(rand):y(rand)}))
当然,它可以正常工作。最终的结果仍然是一个持久数组映射的持久化向量,并具有所有性能影响。(实际上OP使用非常小的散列映射,因此这种方法可能会表现得更好。)@elclanrsfor
理解生成惰性序列。虽然很好,但你不会看到这个答案所展示的那种性能。谢谢,@Michał!我可以通过在程序中使用“数组”来缩短处理时间。