次の 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ミリ秒」
これは3倍遅いです。
今、私の言い換えた質問は次のとおりです。事実上原始的な数学演算であるコード内の関数呼び出しを回避するために、どうすれば clojure コードを構造化/ヒント化できますか?
編集2:
テストを変更して、チェックされていないプリミティブ数学演算子を使用するようにしました。
(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 ミリ秒」なので、ほぼ 2 倍優れていますが、それでも Java バージョンの 1.6 倍です。