任何人都可以解释为什么时间只需将其包装在一个函数中就可以跳跃一个数量级吗?
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)))))
#<Var@54dd6004: #object[user$eval3462$tl__3463 0x7d8ba46 "user$eval3462$tl__3463@7d8ba46"]>
user> (time (tl 10000000))
"Elapsed time: 507.333844 msecs"
49999995000000
我很好奇如何能够更快地完成这样的简单迭代。例如,C ++中类似的迭代循环在Release模式下的时间不到1 ms,或者在与此Clojure代码相同的系统上的Debug模式下大约需要20 ms。
答案 0 :(得分: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)在第一种情况下,你使用java原语操作,这就是为什么它如此之快。 ^Integer
在这里不起作用,因为它是盒装类型java.lang.Integer
的类型提示(这就是它大写的原因)。 ^long
是java long
原语的类型提示。对于函数参数,您可能只执行^long
和^double
基本类型提示(其他不支持除了这些之外,您可以为所有类型的基本数组键入提示,例如{ {1}},^floats
等。)。
3)由于传递的参数是盒装的,因此强制所有操作都采用通用的aritmethics。换句话说,每个^bytes
和+
操作都会在堆上创建新对象(如果是基元,它们将保留在堆栈上)。
UPD 2:
作为类型提示的替代方法,您可以在循环之前显式地将传递的参数转换为基元:
inc