我编写了一个计算量大的函数(下面的get-cand-info),该函数将由其他人编写的现有clojure代码调用。
(defn get-cand-info [model tuple] ; my code which operates on 'tuple' and a hash-map called 'model'
; ....
cand-info)
;; how my code get-cand-info is going to be called
(defn get-cand-scores [model]
(let [tuples (make-tuples model)]
(filter identity
(pmap #(get-cand-info model %) tuples))))
(defn select-cand [model]
(let [cands-with-scores (get-cand-scores model)]
; Logic to work on cand-with-scores, finally returns one of
; the cand-info but not the model
))
在编写了新的get-cand-info函数之后,我意识到对于最终用户会话它会产生数百次相同的结果,这实际上是对资源的浪费。
自然,我倾向于考虑 memoize ,但不想在程序的整个生命周期内增加内存使用量。在所有用户会话中,缓存中可能有很多唯一数据,并且来自一个用户会话的数据无论如何对于另一个用户会话无效。我函数的'model'参数似乎是缓存get-cand-info结果的理想位置,因为它存储一个会话的数据。
但是,如果我从函数返回更新的模型,它将更改函数返回的约定。如果我确实修改了合同以返回带有新结果的新“模型”映射,那么我将需要一直在调用堆栈中一直更新代码-这意味着要更改许多功能以及我想要的东西避免。
因此,我决定更改模型并在节点中对其进行变异:
(defn get-cand [model tuple]
; Fetch the cand-info from the model if available there
(if-let [cand-info ((deref (:cand-info model)) tuple)]
cand-info
; Else calculate the cand-info,
; ....
;store it in the model and return it
(do
(swap! (:cand-info model) assoc tuple cand-info)
cand-info) ))
这可以完成工作,但让我感到疑惑
1)是否有更好,更克洛瑞的解决方法?
2)突变是否可能导致性能下降或其他缺陷? (我还没有大型数据集来测试性能)。
不胜感激。
P.S。用户会话通常不超过5分钟,每个会话要存储在get-cand-info中的数据大小将小于200 MB,会话结束后可以立即将其存储。
答案 0 :(得分:2)
我会按照您的建议去做。为此,无需将dosync
和alter
与ref
一起使用。只需在每个atom
中使用本地model
。然后可以在不再使用模型时进行GC处理。
更新
Java的一种替代方法是使用LinkedHashMap。您可以设置最大大小和override the removeEldestEntry()
function来控制行为。
请注意,此语法略有偏离,但我确定您知道如何解决此问题:
((deref...
答案 1 :(得分:2)
为回答(1),您的get-cand
函数看起来与usage中的clojure.core/cache
具有非常相似的结构。您可以查看FIFO / LIFO / TTL缓存,这些缓存也可以简单地包装在一个原子中以进行更新。
关于(2),虽然我认为这实际上不算是“变异”(也许专家可以澄清),但我认为缓存大量的预先计算(毕竟Lookup table s是完全明智的)在图像处理等领域非常普遍,它们基本上是预先水化的缓存。)