首页 > 解决方案 > 在这种情况下突变的替代方案?

问题描述

我编写了一个计算密集型函数(下面的 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,但不想在程序的整个生命周期中增加内存使用量;在所有用户会话中,缓存中可能有很多唯一数据,并且来自一个用户会话的数据对另一个用户会话无效。我的函数的“模型”参数似乎是缓存 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)是否有更好,更clojurey的方法来解决问题?

2)突变是否可能导致任何性能损失或其他缺陷?(我还没有大型数据集来测试性能)。

将不胜感激任何见解/评论。

PS 用户会话通常不超过 5 分钟,并且每个会话要存储在 get-cand-info 中的数据大小将低于 200 MB,一旦会话结束就可以进行 GC。

标签: clojure

解决方案


我会按照你的建议去做。无需为此使用dosync& 。只需在每个. 当模型不再被使用时,它可以被 GC'd。alterrefatommodel


更新

Java 的一种替代方法是使用 LinkedHashMap。您可以设置最大大小并覆盖该removeEldestEntry()函数以控制行为。


请注意,此语法略有偏差,但我相信您知道如何修复它:

((deref...

推荐阅读