我经历了" Clojure编程" Chas Emerick,Brian Carper和Christophe Grand的书。
在关于memoization的部分,我注意到在没有使用memoization的情况下,第二次(和其他)函数调用的时间显着减少了。
当然,使用memoization会减少数量级的时间,但是我很感兴趣,如果Clojure在内部执行类似memoization的事情。
以下是代码:
(defn prime?
[n]
(cond
(== 1 n) false
(== 2 n) true
(even? n) false
:else (->> (range 3 (inc (Math/sqrt n)) 2)
(filter #(zero? (rem n %)))
empty?)))
(def n 123)
(time (prime? n))
(time (prime? n))
(let [m-prime? (memoize prime?)]
(time (m-prime? n))
(time (m-prime? n)))
输出:
"Elapsed time: 0.235977 msecs"
"Elapsed time: 0.054549 msecs"
"Elapsed time: 0.045127 msecs"
"Elapsed time: 0.003814 msecs"
那么,为什么第二次通话比第一次通话快5倍?
答案 0 :(得分:2)
第一次调用任何Clojure函数时,它的速度会变慢。我认为Clojure会延迟一些内部链接或设置,直到调用一个函数。
可能是JIT,但我不这么认为。这将在定义函数时启动。
记忆不是灵丹妙药。如果你所调用的内容并不是真的很慢,那么它实际上可能比执行原始代码要慢。例如,当我编写锦标赛配对算法时,我在重复调用的函数上进行了memoization实验,并且总是返回相同的结果,并且显着降低了匹配算法的速度。记忆化的速度是没有的2-3倍。
答案 1 :(得分:2)
Internaly每个clojure函数都由jvm类表示 - 第一次调用更长,因为类加载器必须加载类。
正如之前的回答所说 - 肯定不是JIT,因为大多数优化的JIT都有一定的阈值 - 可以使用XX更改:CompileThreshold - 默认值设置为10000 - 这是为什么jvm必须是在基准测试之前热身。
请记住,memoization只是缓存。并且函数必须是“纯粹的”才能正常工作 - 编译器极难猜测哪些函数可能会被自动记忆。