2016-05-18 17 views
5

Czy ktoś może wyjaśnić, dlaczego czas skacze o rząd wielkości po prostu zawijając to w funkcję?Dlaczego owijanie tego w funkcję trwa 10 razy dłużej?

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 

Jestem ciekawy, jak po prostu taka iteracja może być wykonana znacznie szybciej. Na przykład podobna iteracyjna pętla w C++ trwa mniej niż 1 ms w trybie Release lub około 20 ms w trybie debugowania w tym samym systemie co ten kod Clojure.

Odpowiedz

9

Dzieje się tak, ponieważ w drugim przypadku przekazywany jest argument. Dodaj typ wskazówkę, aby to naprawić:

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) W pierwszym przypadku działają z prymitywów Java, dlatego jest to tak szybko. ^Integer nie będzie tu działać, ponieważ jest podpowiedź typu dla typu pudełkowego java.lang.Integer (dlatego jest pisany wielką literą). ^long jest podpowiedzią typu dokładnie dla prymitywu java long. W przypadku parametrów funkcji możesz wykonywać tylko ^long i ^double podpowiedzi typu podstawowego ( inne nie są obsługiwane oprócz tych możesz pisać wskazówki dla wszystkich rodzajów prymitywnych tablic, takich jak ^floats, ^bytes itd.).

3) Ponieważ przekazany argument jest zapakowany w polu, wymusza to ogólne działanie arytmetyczne dla wszystkich operacji. Innymi słowy, każda operacja + i utworzy nowy obiekt na stercie (w przypadku prymitywów pozostaną one na stosie).

UPD 2:

Jako alternatywę do typu podpowiedzi można jawnie przekonwertować przekazany argument prymitywny przed pętli:

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

dwa followups: próbowałem to z '^ Integer' bez różnicy ; dlaczego 'long' jest konieczny dla argumentu 10 milionów? i 2) dlaczego 'long' ma małą literę, ale'^Integer' musi być napisane wielkimi literami lub nie będzie kompilowane? 3) skoro argument jest przekazywany tylko raz, przy wywołaniu funkcji, to "rozpakowanie" tylko jeden raz wystarczy, aby spowodować tak duży wzrost czasu? – johnbakers

+0

@johnbakers Przedłużę moją odpowiedź – OlegTheCat

Powiązane problemy