2013-05-20 3 views
4

J'ai une carte simple:ClojureScript carte recherche lente

(def my-map 
    {[1 2 3] 1 
    [1 2 4] 5 
    [3 4 2] 3 
    [4 5 3] 3 
    [5 2 5] 6 
    [9 2 1] 5 
    [8 3 1] 6}) 

que j'utilise pour effectuer des recherches. Cela joue assez mal, cependant:

(time (doseq [x (range 500)] 
     (my-map [1 2 8]))) 

"Elapsed time: 170 msecs" 

Sur la même machine, Clojure peut faire 500 000 à environ 236 msec, soit environ 700x plus rapide. Bien qu'il ne soit pas surprenant que Clojure soit plus rapide que ClojureScript, je ne comprends pas pourquoi ClojureScript serait tellement plus lent.

Des idées sur la façon dont je pourrais faire une carte de recherche multi-valuée simple comme ci-dessus de manière efficace et d'une manière lisible dans ClojureScript? Je sais que faire un tas de if s au lieu d'utiliser une solution de clé vectorielle fonctionnerait certainement plus rapidement, mais je regarde quelque chose qui est un peu plus lisible/maintenable.

Juste pour mettre à jour avec plus d'informations. Ce qui précède a été fait dans Firefox, donc plus lent que par rapport à V8. Ce qui suit:

(def my-map2 
    (into cljs.core.PersistentHashMap/EMPTY 
     {[1 2 3] 1 
     [1 2 4] 5 
     [3 4 2] 3 
     [4 5 3] 3 
     [5 2 5] 6 
     [9 2 1] 5 
     [8 3 1] 6})) 

(defn p1 [] 
    (let [v [1 2 8]] 
    (dotimes [_ 5] 
     (time (dotimes [_ 500000] 
       (get my-map2 v)))))) 

donne:

"Elapsed time: 3295 msecs" 

"Elapsed time: 3246 msecs" 

"Elapsed time: 3113 msecs" 

"Elapsed time: 3107 msecs" 

"Elapsed time: 3121 msecs" 

en chrome Version 25.0.1364.160 Ubuntu 13.04 (25.0.1364.160-0ubuntu3). Donc encore environ 13 fois plus lent dans ClojureScript que Clojure, mais c'est beaucoup mieux que ce qu'il était auparavant. Notez également que je cours cela directement dans le navigateur repl.

+0

Oui Firefox est un peu moins performant en termes de performances ClojureScript. J'espère que nous soumettrons bientôt un code de référence à Mozilla pour aider à résoudre ce problème. Le code JavaScript généré par le navigateur REPL n'est pas entièrement optimisé car cela entrave le développement. Donc, pour vraiment mesurer les performances de ClojureScript, vous devez utiliser une compilation avancée. – dnolen

Répondre

7

Sur mon ordinateur exécuter votre exemple exact avec la compilation avancée prend ~ 14ms sur mon Macbook Air 1.7ghz fonctionnant sur un v8 relativement récent construit à partir de la source.

Pour nous assurer l'analyse comparative que nous pensons que nous sommes l'analyse comparative, il est préférable d'écrire quelque chose comme ceci:

(let [v [1 2 8]] 
    (dotimes [_ 5] 
    (time 
     (dotimes [_ 500000] 
     (get my-map v))))) 

Sur ma machine, cela prend ~ 70ms sur la machine pour la JVM Clojure. ClojureScript tourne autour de ~ 3600ms, donc environ 50X plus lentement. Pourquoi? C'est parce que nous avons par défaut PersistentArrayMap où Clojure ne le fait pas lorsque définit petites cartes de hachage avec des clés complexes.

Que se passe si nous définissons ma-carte comme ceci:

(def my-map 
    (into cljs.core.PersistentHashMap/Empty 
    [[1 2 3] 1 
    [1 2 4] 5 
    [3 4 2] 3 
    [4 5 3] 3 
    [5 2 5] 6 
    [9 2 1] 5 
    [8 3 1] 6])) 

L'indice de référence prend alors ~ 170ms, ce qui est pas si loin de JVM Clojure.

Il y a donc beaucoup d'optimisations que Clojure implémente et pour lesquelles nous n'avons pas encore trouvé de solution. Je dirais que pour le code Clojure idiomatique, je pense que le meilleur que nous pouvons espérer sur les moteurs JavaScript très affinés comme V8 est de 2-10X de Clojure JVM.

+0

Merci dnolen! Voir la mise à jour avec plus de détails pour faire la lumière sur les différences. On dirait que j'ai juste besoin de passer à Chromium pour le moment;) et utilisez votre suggestion pour utiliser Hash vs. Array ici - bon à savoir. –