2016-04-22 13 views
4

Ich kam in diesem während einig Performance-sensitiven Code-Tuning:Warum wird get-in langsamer als durchgefädelt?

user> (use 'criterium.core) 
nil 
user> (def n (into {} (for [i (range 20000) :let [k (keyword (str i))]] [k {k k}]))) 
#'user/n 
user> (quick-bench (-> n :1 :1)) 
WARNING: Final GC required 32.5115186521176 % of runtime 
Evaluation count : 15509754 in 6 samples of 2584959 calls. 
Execution time mean : 36.256135 ns 
Execution time std-deviation : 1.076403 ns 
Execution time lower quantile : 35.120871 ns (2.5%) 
Execution time upper quantile : 37.470993 ns (97.5%) 
       Overhead used : 1.755171 ns 
nil 
user> (quick-bench (get-in n [:1 :1])) 
WARNING: Final GC required 33.11057826481865 % of runtime 
Evaluation count : 7681728 in 6 samples of 1280288 calls. 
Execution time mean : 81.023429 ns 
Execution time std-deviation : 3.244516 ns 
Execution time lower quantile : 78.220643 ns (2.5%) 
Execution time upper quantile : 85.906898 ns (97.5%) 
       Overhead used : 1.755171 ns 
nil 

Es ist mir nicht intuitiv, dass get-in zweimal mehr ist, als so langsam wie durch get Einfädeln s hier als get-in scheint als die bessere Abstraktion für diese definiert werden irgendwie.

Hat jemand einen Einblick, warum dies der Fall ist (sowohl technisch als auch philosophisch)?

+1

Wenn Sie an der Quelle für diese beiden Funktionen anschaut, kann man sehen, dass es einige Rekursion in los [ 'get-in'] (https://github.com/clojure/clojure/blob/clojure-1.7.0 /src/clj/clojure/core.clj#L5882), die es ermöglicht, verschachtelte Strukturen zu durchforsten. Ich schätze, das ist die Ursache für den zusätzlichen Overhead. – jmargolisvt

+0

Ich kann nur raten, dass Get-In eine "Allzweck" -Funktion ist und mehr Overhead hat. Auf meinem Computer ist es nicht ganz so schlimm, bei 31 ns vs 57 ns. –

Antwort

7

Verschachtelte Karten sind sehr häufig in Clojure-Programmen verwendet. Das ist eine gute Sache. Aber es kann Situationen geben, in denen verschachtelte Karte Operationen wie assoc-in und get-in können durch Abrollen verbessert werden. (get :a (get :b (get :c (get :d m))) ist nicht dasselbe wie (get-in m [:d :c :b :a]) in Bezug auf den Byte-Code produziert. Der Bytecode des letzteren führt zu einer schlechteren Ausführungszeit.

Beachten Sie, dass Clojure einige anhängige Patches http://dev.clojure.org/jira/browse/CLJ-1656 hat im Zusammenhang mit dieser.

+1

Ich glaube nicht, rekursive Funktionsaufrufe sind das Problem, so viel wie die vielen Vektoren, die zugewiesen werden müssen. – amalloy

Verwandte Themen