2010-09-27 5 views
8

Ce code Java peut-il être traduit en un code Clojure aussi rapide ou presque aussi rapide?Transformer le code Java en code Clojure rapide

J'ai pu obtenir des fonctions plus simples comme l'ajout de deux tableaux pour fonctionner à des vitesses raisonnables avec indication de type, mais je ne pouvais pas faire fonctionner Clojure comme le font les fonctions ci-dessous dans un laps de temps raisonnable Java interop ou Incanter matrices et en utilisant des styles fonctionnels ou impératifs. Est-ce que je manque quelque chose à propos de l'indication de type ou est-il préférable de faire ce genre de chose en 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; 
} 

Répondre

5

commençant par la branche clojure 1.3 actuellement sur github vous pouvez use primitives as arguments to functions et retours de fonctions. Vous n'aurez plus à taper les primitives de nombre d'indices. Il devrait vraiment faire allusion à ce type de code beaucoup plus rapide et regarder beaucoup plus élégant. Dans l'indication de type cela, vous pourriez être confronté au fait que (< = clojure 1.2) tous les arguments de la fonction ont été encadrés.

+0

Qu'est-ce que cela signifie exactement que le les arguments de la fonction sont encadrés? J'ai été en mesure d'obtenir le code de clojure qui ajoute deux tableaux java 2d pour fonctionner assez proche des vitesses natives. Comme je le comprends maintenant, la fonction tire des nombres de Java, les ajoute dans clojure, puis repousse le résultat dans un tableau java, et le type y fait allusion pour les interactions clojure to java. Mais mes versions clojure du code java font la même chose, mais il y a plus d'opérations dans clojure avant que je ne ramène les choses à Java. Mais je ne comprends pas pourquoi les opérations supplémentaires rendraient les choses tellement plus lentes. – 2daaa

+0

Quand je dis appel de fonction, je fais référence à l'appel d'une fonction de clojure. Si vous exécutez (my-function 42 :-P) le nombre 42 est une instance de classe Integer et non un int primitif, même si vous tapez hint it. aussi le type de retour d'un appel de fonction clojure est toujours un objet et jamais une primitive (avant clojure 1.3) La transition vers clojure 1.3 peut laisser tomber un zéro du temps de fonctionnement de certaines fonctions fortement numériques. –

+0

Sans indication de type, comment traiteriez-vous les collections de types mixtes? –

3

L'autre élément qui aidera (également en 1.3) est la liaison de fonction statique, qui va faire certains appels de fonction aussi vite que les appels de méthode (ceci est également décrit dans le lien Arthur posté). Il sera toujours difficile d'écrire ce code d'une manière vraiment idiomatique (c'est-à-dire en utilisant la fonction d'ordre supérieur "map") pour des performances Java complètes, car les fonctions d'ordre supérieur ne pourront pas utiliser les fonctions statiques. la liaison, mais (avertissement d'un bouchon sans vergogne) c'est quelque chose Rich Hickey veut rectifier:

http://combinate.us/clojure/2010/09/27/clojure/

+0

ce serait vraiment bien de mapper des fonctions sur les primitives. –

+0

Les fonctions statiques sont une condition préalable à l'utilisation de primitives en tant qu'arguments ou types de retour. –

+0

la façon dont ils sont actuellement mis en œuvre, c'est vrai. Rich a établi une distinction au cours du meetup SF, notant qu'au niveau de la JVM, ils ne sont pas strictement couplés - il semble que le support primitif qu'il met en œuvre puisse à un moment donné ne pas être étroitement lié aux fonctions statiques. – tvachon

8

Effectuez les opérations suivantes dans clojure 1.3 (branche principale):

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

J'ai essayé de créer un tableau: (def A (array-array (mappage double carte (répète L% 1) (plage L))))), puis votre fonction grad2: (temps (grad2 A 1 1)) mais il abandonne juste et je ne vois aucune trace de pile pour une raison quelconque. – 2daaa