2016-05-18 9 views
5

Qualcuno può spiegare perché il tempo salta di un ordine di grandezza semplicemente avvolgendolo in una funzione?Perché eseguire il wrapping di questa funzione richiede 10 volte di più?

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 

Sono curioso come una semplice iterazione di come questo può essere fatto molto più veloce. Ad esempio, un loop iterativo simile in C++ impiega meno di 1 ms in modalità Release o circa 20 ms in modalità Debug sullo stesso sistema di questo codice Clojure.

risposta

9

Ciò accade perché nel secondo caso l'argomento passato viene inserito. Aggiungi tipo di suggerimento per risolvere questo problema:

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) Nel primo caso si opera con primitive Java, è per questo che è così veloce. ^Integer non funzionerà qui, perché è il tipo suggerimento per il tipo scatolato java.lang.Integer (è per questo che è in maiuscolo). ^long è tipo hint esattamente per java long primitivo. Per i parametri di funzionamento è possibile eseguire solo ^long e ^double suggerimenti di tipo primitivo ( altri non sono supportati oltre a questi si possono fare suggerimenti tipo per tutti i tipi di matrici primitive, come ^floats, ^bytes ecc.).

3) Poiché l'argomento passato è racchiuso tra parentesi, questo impone l'aritmetica generica per tutte le operazioni. In altre parole, ogni operazione e inc creerà un nuovo oggetto sull'heap (in caso di primitive rimarranno nello stack).

UPD 2:

In alternativa al tipo hinting è possibile convertire in modo esplicito argomento passato alla primitiva prima che il ciclo:

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

due followup: avevo provato questo con '^ Integer' senza differenze ; perché è "lungo" necessario per un argomento di dieci milioni? e 2) perché è "lungo" in minuscolo ma "^ Integer" deve essere in maiuscolo o non verrà compilato? 3) poiché l'argomento viene passato una sola volta, durante la chiamata di funzione, questo "unboxing" è sufficiente una sola volta per causare un così enorme aumento di tempo? – johnbakers

+0

@johnbakers Estenderò la mia risposta – OlegTheCat

Problemi correlati