gpt4 book ai didi

optimization - Clojure 中的快速复数运算

转载 作者:行者123 更新时间:2023-12-03 15:27:14 27 4
gpt4 key购买 nike

我在 Clojure 中实现了一些基本的复数运算,并注意到它比大致等效的 Java 代码慢了大约 10 倍,即使有类型提示也是如此。

比较:

(defn plus [[^double x1 ^double y1] [^double x2 ^double y2]]
[(+ x1 x2) (+ y1 y2)])

(defn times [[^double x1 ^double y1] [^double x2 ^double y2]]
[(- (* x1 x2) (* y1 y2)) (+ (* x1 y2) (* y1 x2))])

(time (dorun (repeatedly 100000 #(plus [1 0] [0 1]))))
(time (dorun (repeatedly 100000 #(times [1 0] [0 1]))))

输出:
"Elapsed time: 69.429796 msecs"
"Elapsed time: 72.232479 msecs"

和:
public static void main( String[] args ) {
double[] z1 = new double[] { 1, 0 };
double[] z2 = new double[] { 0, 1 };
double[] z3 = null;

long l_StartTimeMillis = System.currentTimeMillis();
for ( int i = 0; i < 100000; i++ ) {
z3 = plus( z1, z2 ); // assign result to dummy var to stop compiler from optimising the loop away
}
long l_EndTimeMillis = System.currentTimeMillis();
long l_TimeTakenMillis = l_EndTimeMillis - l_StartTimeMillis;
System.out.format( "Time taken: %d millis\n", l_TimeTakenMillis );


l_StartTimeMillis = System.currentTimeMillis();
for ( int i = 0; i < 100000; i++ ) {
z3 = times( z1, z2 );
}
l_EndTimeMillis = System.currentTimeMillis();
l_TimeTakenMillis = l_EndTimeMillis - l_StartTimeMillis;
System.out.format( "Time taken: %d millis\n", l_TimeTakenMillis );

doNothing( z3 );
}

private static void doNothing( double[] z ) {

}

public static double[] plus (double[] z1, double[] z2) {
return new double[] { z1[0] + z2[0], z1[1] + z2[1] };
}

public static double[] times (double[] z1, double[] z2) {
return new double[] { z1[0]*z2[0] - z1[1]*z2[1], z1[0]*z2[1] + z1[1]*z2[0] };
}

输出:
Time taken: 6 millis
Time taken: 6 millis

事实上,类型提示似乎没有什么区别:如果我删除它们,我会得到大致相同的结果。真正奇怪的是,如果我在没有 REPL 的情况下运行 Clojure 脚本,我会得到较慢的结果:
"Elapsed time: 137.337782 msecs"
"Elapsed time: 214.213993 msecs"

所以我的问题是:如何才能接近 Java 代码的性能?为什么在没有 REPL 的情况下运行 clojure 时表达式需要更长的时间来评估?

更新 ===============

太好了,使用 deftype deftype 中有类型提示在 defn s,并使用 dotimes而不是 repeatedly提供与 Java 版本一样好或更好的性能。感谢你们俩。
(deftype complex [^double real ^double imag])

(defn plus [^complex z1 ^complex z2]
(let [x1 (double (.real z1))
y1 (double (.imag z1))
x2 (double (.real z2))
y2 (double (.imag z2))]
(complex. (+ x1 x2) (+ y1 y2))))

(defn times [^complex z1 ^complex z2]
(let [x1 (double (.real z1))
y1 (double (.imag z1))
x2 (double (.real z2))
y2 (double (.imag z2))]
(complex. (- (* x1 x2) (* y1 y2)) (+ (* x1 y2) (* y1 x2)))))

(println "Warm up")
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1)))))

(println "Try with dorun")
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1)))))

(println "Try with dotimes")
(time (dotimes [_ 100000]
(plus (complex. 1 0) (complex. 0 1))))

(time (dotimes [_ 100000]
(times (complex. 1 0) (complex. 0 1))))

输出:
Warm up
"Elapsed time: 92.805664 msecs"
"Elapsed time: 164.929421 msecs"
"Elapsed time: 23.799012 msecs"
"Elapsed time: 32.841624 msecs"
"Elapsed time: 20.886101 msecs"
"Elapsed time: 18.872783 msecs"
Try with dorun
"Elapsed time: 19.238403 msecs"
"Elapsed time: 17.856938 msecs"
Try with dotimes
"Elapsed time: 5.165658 msecs"
"Elapsed time: 5.209027 msecs"

最佳答案

性能缓慢的可能原因是:

  • Clojure 向量本质上是比 Java double[] 数组更重量级的数据结构。所以你在创建和读取向量时有相当多的额外开销。
  • 您将 double 作为函数的参数以及将它们放入向量中时。在这种低级数字代码中,装箱/拆箱相对昂贵。
  • 类型提示 ( ^double ) 对您没有帮助:虽然您可以在普通 Clojure 函数上使用原始类型提示,但它们不适用于向量。

  • 看到这个 blog post on accelerating primitive arithmetic了解更多详情。

    如果你真的想要 Clojure 中的快速复数,你可能需要使用 deftype 来实现它们。 , 就像是:
    (deftype Complex [^double real ^double imag])

    然后使用这种类型定义所有复杂的函数。这将使您能够自始至终使用原始算术,并且应该大致相当于编写良好的 Java 代码的性能。

    关于optimization - Clojure 中的快速复数运算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11824815/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com