我尝试重写memoize
以便在单个线程上使用瞬态而不是原子来查看它在我的场景中是否更快但它仅适用于前8个调用。为什么?这是代码(Clojure 1.8):
(defn transient-memoize [f]
(let [mem (transient {})]
(fn [& args]
(let [v (mem args ::none)]
(if (= v ::none)
(let [ret (apply f args)]
(assoc! mem args ret)
ret)
v)))))
(defn slow-add [x y]
(println "slow-add" x y)
(Thread/sleep 10)
(+ x y))
(defn call-add [memoizer]
(let [f (memoizer slow-add)]
(reduce +
(for [i (range 10)
_ (range 2)]
(f 100 i)))))
(call-add memoize)
slow-add 100 0
slow-add 100 1
slow-add 100 2
slow-add 100 3
slow-add 100 4
slow-add 100 5
slow-add 100 6
slow-add 100 7
slow-add 100 8
slow-add 100 9
=> 2090
(call-add transient-memoize)
slow-add 100 0
slow-add 100 1
slow-add 100 2
slow-add 100 3
slow-add 100 4
slow-add 100 5
slow-add 100 6
slow-add 100 7
slow-add 100 8
slow-add 100 8 ; <- called second time
slow-add 100 9
slow-add 100 9 ; <- called second time
=> 2090
更新
如果我初始化瞬态地图,它确实有效,因此它是TransientHashMap
而不是TransientArrayMap
。与普通地图不同,瞬态地图似乎不会随着集合的增长从ArrayMap
自动转换为HashMap
。总是这样吗?
(defn transient-memoize [f]
(let [mem (transient (hash-map ::force-hash-map ::force-hash-map))]
(fn [& args]
(let [v (mem args ::none)]
(if (= v ::none)
(let [ret (apply f args)]
(assoc! mem args ret)
ret)
v)))))