2016-05-18 10 views
5

Kann jemand erklären, warum die Zeit um eine Größenordnung springt, einfach indem man das in eine Funktion einwickelt?Warum dauert es 10x länger, um diese Funktion zu verwenden?

user> (time (loop [n 0 t 0] 
       (if (= n 10000000) 
       t 
       (recur (inc n) (+ t n))))) 
"Elapsed time: 29.312145 msecs" 
49999995000000 

user> (defn tl [r] (loop [n 0 t 0] 
        (if (= n r) 
         t 
         (recur (inc n) (+ t n))))) 
#<[email protected]: #object[user$eval3462$tl__3463 0x7d8ba46 "[email protected]"]> 

user> (time (tl 10000000)) 
"Elapsed time: 507.333844 msecs" 
49999995000000 

Ich bin gespannt, wie eine einfache Iteration wie diese viel schneller gemacht werden kann. Zum Beispiel benötigt eine ähnliche iterative Schleife in C++ weniger als 1 ms im Freigabemodus oder etwa 20 ms im Debugmodus auf demselben System wie dieser Clojure-Code.

Antwort

9

Dies passiert, weil im zweiten Fall übergeben Argument wird boxed. In Art Hinweis, dies zu beheben:

user> (defn tl [^long r] 
    (loop [n 0 t 0] 
    (if (= n r) 
     t 
     (recur (inc n) (+ t n))))) 

user> (time (tl 10000000)) 
"Elapsed time: 20.268396 msecs" 
49999995000000 

UPD:

1, 2) Im ersten Fall, dass Sie mit Java-Primitive arbeiten, das ist, warum es so schnell ist. ^Integer wird hier nicht funktionieren, weil es Typ Hinweis für Boxed Typ java.lang.Integer (deshalb ist es groß geschrieben). ^long ist Typ Hinweis genau für Java long primitiv. Für Funktionsparameter können Sie nur ^long und ^double primitive Typ Hinweise ( andere werden nicht unterstützt neben diesen können Sie Hinweise für alle Arten von primitiven Arrays, wie ^floats, ^bytes usw. Tipp).

3) Da das übergebene Argument eingerahmt ist, werden generische Arithmetik für alle Operationen erzwungen. Mit anderen Worten, jede Operation + und inc erzeugt ein neues Objekt auf dem Heap (im Falle von Primitiven verbleiben sie auf dem Stapel).

UPD 2:

Als Alternative können Sie explizit übergebene Argument zu primitiv konvertieren, bevor die Schleife eingeben anspielend:

user> (defn tl [r] 
    (let [r (long r)] 
    (loop [n 0 t 0] 
     (if (= n r) 
     t 
     (recur (inc n) (+ t n)))))) 

user> (time (tl 10000000)) 
"Elapsed time: 18.907161 msecs" 
49999995000000 
+0

zwei followups: ich das versucht hatte, mit '^ Integer' ohne Unterschied ; Warum ist 'lang' für ein arg von zehn Millionen notwendig? und 2) warum ist "long" kleingeschrieben, aber "^ Integer" muss groß geschrieben werden oder wird nicht kompiliert? 3) Da der Arg nur einmal beim Funktionsaufruf übergeben wird, reicht dieses "Unboxing" nur einmal aus, um einen so großen Zeitgewinn zu verursachen? – johnbakers

+0

@johnbakers Ich werde meine Antwort erweitern – OlegTheCat

Verwandte Themen