Clojure连续函数调用和memoization

时间:2015-12-03 21:09:29

标签: clojure memoization

我经历了" 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倍?

2 个答案:

答案 0 :(得分:2)

第一次调用任何Clojure函数时,它的速度会变慢。我认为Clojure会延迟一些内部链接或设置,直到调用一个函数。

可能是JIT,但我不这么认为。这将在定义函数时启动。

记忆不是灵丹妙药。如果你所调用的内容并不是真的很慢,那么它实际上可能比执行原始代码要慢。例如,当我编写锦标赛配对算法时,我在重复调用的函数上进行了memoization实验,并且总是返回相同的结果,并且显着降低了匹配算法的速度。记忆化的速度是没有的2-3倍。

答案 1 :(得分:2)

Internaly每个clojure函数都由jvm类表示 - 第一次调用更长,因为类加载器必须加载类。

正如之前的回答所说 - 肯定不是JIT,因为大多数优化的JIT都有一定的阈值 - 可以使用XX更改:CompileThreshold - 默认值设置为10000 - 这是为什么jvm必须是在基准测试之前热身。

请记住,memoization只是缓存。并且函数必须是“纯粹的”才能正常工作 - 编译器极难猜测哪些函数可能会被自动记忆。

相关问题