2012-03-26 5 views
5

Ho una domanda sul perché c'è una tale differenza di velocità tra il metodo loop e il metodo iterato in clojure. Ho seguito il tutorial http://www.learningclojure.com/2010/02/clojure-dojo-2-what-about-numbers-that.html e definiti due metodi radice quadrata utilizzando il metodo Heron:Perché c'è una tale differenza di velocità tra il loop di Clojure ei metodi iterate

(defn avg [& nums] (/ (apply + nums) (count nums))) 
(defn abs [x] (if (< x 0) (- x) x)) 
(defn close [a b] (-> a (- b) abs (< 1e-10))) 

(defn sqrt [num] 
    (loop [guess 1] 
    (if (close num (* guess guess)) 
     guess 
    (recur (avg guess (/ num guess))) 
))) 

(time (dotimes [n 10000] (sqrt 10))) ;;"Elapsed time: 1169.502 msecs" 


;; Calculation using the iterate method 
(defn sqrt2 [number] 
    (first (filter #(close number (* % %)) 
     (iterate #(avg % (/ number %)) 1.0)))) 

(time (dotimes [n 10000] (sqrt2 10))) ;;"Elapsed time: 184.119 msecs" 

C'è a circa aumento x10 di velocità tra i due metodi e sto chiedendo cosa accade sotto la superficie a causa i due sono così pronunciati?

+0

presumo prima 'sqrt2' è un errore di battitura? inoltre, è necessario ripetere i tempi più volte per ottenere risultati utili (il jvm richiede tempo per essere ottimizzato). non cambia il fatto che uno è più lento, ma cambia significativamente i numeri. –

+0

Sì ... risolto ora ... quindi 10000 volte non è abbastanza? – zcaudate

+0

non so per certo, ma sembra essere basato sul tempo - come, diventa più veloce nel primo secondo circa. hai provato a ripeterlo e a guardare l'output di time()? –

risposta

5

I risultati sono sorprendenti: normalmente loop/recur è il costrutto più veloce in Clojure per il looping.

Ho il sospetto che JIT JVM abbia elaborato un'ottimizzazione intelligente per il metodo iterato, ma non per la versione loop/recur. È sorprendente quanto spesso ciò avvenga quando si utilizza un codice funzionale pulito in Clojure: sembra essere molto suscettibile all'ottimizzazione.

Si noti che è possibile ottenere un aumento di velocità sostanziale in entrambe le versioni in modo esplicito utilizzando raddoppia:

(set! *unchecked-math* true) 

(defn sqrt [num] 
    (loop [guess (double 1)] 
    (if (close num (* guess guess)) 
     guess 
     (recur (double (avg guess (/ num guess))))))) 

(time (dotimes [n 10000] (sqrt 10))) 
=> "Elapsed time: 25.347291 msecs" 


(defn sqrt2 [number] 
    (let [number (double number)] 
    (first (filter #(close number (* % %)) 
     (iterate #(avg % (/ number %)) 1.0))))) 

(time (dotimes [n 10000] (sqrt 10))) 
=> "Elapsed time: 32.939526 msecs" 

Come previsto, la versione loop/ricorrono ora ha un leggero vantaggio. I risultati sono per Clojure 1.3

+0

Sicuramente accelera la funzione loop/ricorrenza ... cosa fa il flag * unchecked-math *? – zcaudate

+0

disattiva il controllo di overflow per le operazioni primitive. utile per una piccola quantità di velocità extra, il lato negativo è che non otterrai un'eccezione se si verifica un overflow, quindi devi essere un po 'più attento. – mikera

+0

Fantastico! sicuramente accelera la funzione loop/recur ... ho 38.0ms per sqrt e 67.3ms sqrt2 ... =) – zcaudate

Problemi correlati