2016-05-18 2 views
5

Может ли кто-нибудь объяснить, почему время скачет на порядок, просто обернув это функцией?Зачем это делать в функции за 10 раз дольше?

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 

Мне любопытно, как простая итерация, как это, может быть выполнена намного быстрее. Например, аналогичный итеративный цикл в C++ занимает менее 1 мс в режиме деблокирования или около 20 мс в режиме отладки в той же системе, что и этот код Clojure.

ответ

9

Это происходит потому, что во втором случае аргумент передается в поле. Добавить тип подсказки, чтобы исправить это:

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) В первом случае вы работаете с ява примитивов, вот почему это так быстро. ^Integer не будет работать здесь, потому что это тип подсказки для вложенного типа java.lang.Integer (вот почему он капитализирован). ^long есть тип подсказка точно для java long примитив. Для параметров функции вы можете делать только ^long и ^double примитивные подсказки ( другие не поддерживаются кроме этого вы можете вводить подсказки для всех типов примитивных массивов, например ^floats, ^bytes и т. Д.).

3) Поскольку переданный аргумент помещен в коробку, это создает общую арифметику для всех операций. Другими словами, каждая операция + и inc создаст новый объект в куче (в случае примитивов они останутся в стеке).

UPD 2:

В качестве альтернативы типа намекая, вы можете явно преобразовать переданный аргумент примитивных перед циклом:

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

два followups: Я попытался это с '^ Integer' без разницы ; почему «длинный» необходим для десяти процентов? и 2) почему «длинный» нижний регистр, но '^ Integer' должен быть капитализирован или не будет компилироваться? 3), поскольку аргумент arg передается только один раз, при вызове функции этого «unboxing» один раз достаточно, чтобы вызвать такое огромное увеличение времени? – johnbakers

+0

@ johnbakers Я продолжу свой ответ – OlegTheCat

Смежные вопросы