假设我有以下Clojure代码:
(defn foo ^double []
(-
(* 123.31
(+ 4 5 6 (Math/sin 34.2))
123.31)
123))
gen-class是否会生成相当于编译以下java代码的字节代码:
public static double foo(){
return (123.31 * (4 + 5 + 6 + Math.sin(34.2)) * 123.31) - 123;
}
或者换句话说,我可以将Clojure用作非常方便的DSL来生成高效的动态字节代码吗?
修改:
好的,我做了一些测试来说明我的问题:
这是java版本:
public class FooTest {
public static double foo(double a, double b, double c){
return (a * (b + c + (b*c) + Math.sin(a)) * Math.log(b)) - b;
}
public static long benchmark(){
long start = System.currentTimeMillis();
for (double i = 0; i < 100000000.0; i++) { // 100 mln
double r = foo(i, i+1, i+2);
}
long end = System.currentTimeMillis();
return (end-start);
}
public static void main(String[] args) {
System.out.println("Time took: "+benchmark());
}
}
这会产生输出:时间:39200
clojure'等效':
(defn foo ^double
(^double [a b c]
(-
(* a
(+ b c (* b c) (Math/sin a))
(Math/log b))
b)))
(time
(loop [i 0.0]
(when (< i 100000000)
(foo i (+ i 1) (+ i 2))
(recur (inc i)))))
产生:“经过的时间:121242.902 msecs”
慢3倍。
现在我的改述问题是:我如何构造/提示clojure代码,以避免代码中的函数调用,这是有效的原始数学运算?
EDIT2:
我已更改测试,因此它使用未经检查的原始数学运算符:
(defn foo ^double
(^double [a b c]
(binding [*unchecked-math* true]
(-
(* a
(+ b c (* b c) (Math/sin a))
(Math/log b))
b))))
“经过的时间:64386.187 msecs” 所以它几乎要好2倍,但仍然是java版本的1.6倍。
答案 0 :(得分:2)
除了Clojure编译器之外还有一些内容,因为JVM和热点JIT也可以优化代码。当所有值都是原始值且任何变量具有原始类型提示时,Clojure编译器会生成原始数学运算。 在此之后,一旦代码在JVM上运行,Hotspot优化器就会执行内嵌。
ps:使用或不使用gen-class
在此过程中没有任何区别。所有Clojure代码都以相同的方式编译和运行,除了gen-class导致包含字节代码的文件也被创建
答案 1 :(得分:0)
好的,我终于得到了与java相同的Clojure性能。 需要改变三件事:
结果代码是:
(binding [*unchecked-math* true]
(defn foo ^double [^double a ^double b ^double c]
(-
(* a
(+ b c (* b c) (Math/sin a))
(Math/log b))
b)))
(binding [*unchecked-math* true]
(time
(loop [i (double 0.0)]
(when (< i 100000000)
(foo i (+ i 1) (+ i 2))
(recur (inc i))))))