在这种情况下,突变的替代方案?

时间:2019-01-15 21:10:20

标签: clojure

我编写了一个计算量大的函数(下面的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,会话结束后可以立即将其存储。

2 个答案:

答案 0 :(得分:2)

我会按照您的建议去做。为此,无需将dosyncalterref一起使用。只需在每个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是完全明智的)在图像处理等领域非常普遍,它们基本上是预先水化的缓存。)