Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Turning Java code into fast Clojure code

Tags:

clojure

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;
}
like image 996
2daaa Avatar asked Sep 27 '10 20:09

2daaa


3 Answers

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))
like image 178
Alex Taggart Avatar answered Nov 12 '22 20:11

Alex Taggart


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.

like image 36
Arthur Ulfeldt Avatar answered Nov 12 '22 20:11

Arthur Ulfeldt


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/

like image 3
tvachon Avatar answered Nov 12 '22 21:11

tvachon