Clojure是否有效地内联原始操作?

时间:2013-06-28 16:50:12

标签: clojure gen-class

假设我有以下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倍。

2 个答案:

答案 0 :(得分:2)

除了Clojure编译器之外还有一些内容,因为JVM和热点JIT也可以优化代码。当所有值都是原始值且任何变量具有原始类型提示时,Clojure编译器会生成原始数学运算。 在此之后,一旦代码在JVM上运行,Hotspot优化器就会执行内嵌

ps:使用或不使用gen-class在此过程中没有任何区别。所有Clojure代码都以相同的方式编译和运行,除了gen-class导致包含字节代码的文件也被创建

答案 1 :(得分:0)

好的,我终于得到了与java相同的Clojure性能。 需要改变三件事:

  1. 正确提示函数参数(之前我暗示回复 值不是函数的参数)
  2. 我把绑定移出了 功能体。
  3. 在中使用未经检查的数学运算 微型基准助手
  4. 结果代码是:

    (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))))))