Can this Java code be translated to Clojure code that's as fast or nearly as fast?
I've been able to get simpler functions like adding two arrays to run at reasonable speeds with type hinting, but I couldn't get Clojure to do what the functions below do at all in a reasonable amount of time using either Java interop or Incanter matrices and using either functional or imperative styles.
Am I missing something about type hinting or is it just best to do this kind of thing in Java?
static double[][] grad2_stencil= { {0,0,-1,0,0},
{0,0,16,0,0},
{-1,16,-60,16,-1},
{0,0,16,0,0},
{0,0,-1,0,0} };
public static double grad2(double[][] array, int x, int y){
double temp=0;
int L=array.length;
for(int i=0; i<5; i++){
for(int j=0; j<5; j++){
temp+=array[((x+i-2)%L+L)%L][((y+j-2)%L+L)%L]*grad2_stencil[i][j];
}
}
return temp/12.0;
}
public static double[][] grad2_field(double[][] arr){
int L=arr.length;
double[][] result=new double[L][L];
for(int i=0; i<L; i++){
for(int j=0; j<L; j++){
result[i][j]=grad2(arr, i, j);
}
}
return result;
}
Try the following in clojure 1.3 (master branch):
(def ^"[[D" grad2-stencil
(into-array (Class/forName "[D")
(map double-array
[[ 0 0 -1 0 0 ]
[ 0 0 16 0 0 ]
[-1 16 -60 16 -1 ]
[ 0 0 16 0 0 ]
[ 0 0 -1 0 0 ]])))
(defn ^:static idx ^long [^long x ^long i ^long L]
(-> x
(+ i)
(- 2)
(mod L)
(+ L)
(mod L)))
(defn ^:static grad2 ^double [^doubles arr ^long x ^long y]
(let [L (alength arr)
temp (loop [i 0 j 0 temp 0.0]
(if (< i 5)
(let [a (idx x i L)
b (idx y j L)
temp (double (* (aget arr a b)
(aget grad2-stencil i j)))]
(if (< j 4)
(recur i (inc j) temp)
(recur (inc i) 0 temp)))
temp))]
(/ temp 12.0)))
(defn ^:static grad2-field ^"[[D" [^"[[D" arr]
(let [result (make-array Double/TYPE (alength arr) (alength arr))]
(loop [i 0 j 0]
(when (< i 5)
(aset result (grad2 arr i j) i j)
(if (< j 4)
(recur i (inc j))
(recur (inc i) 0))))
result))
starting with the clojure 1.3 branch currently on github you can use primitives as arguments to functions and returns from functions. You also will no longer have to type hint number primitives. It should really make hinting this type of code a lot faster and look a lot more elegant.
In type hinting this you could be running up against the fact that (<= clojure 1.2) all function arguments were boxed.
The other piece that will help (also in 1.3) is static function linking, which will make some function calls as fast as method calls (this is also described in the link Arthur posted).
It'll still be difficult to write this code in a truly idiomatic way (ie, using the "map" higher order function) for now at full java performance, since higher order functions won't be able to use static linking, but (shameless plug warning) this is something Rich Hickey wants to rectify:
http://combinate.us/clojure/2010/09/27/clojure/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With