Альтернативы мутации в этом сценарии? - PullRequest
0 голосов
/ 16 января 2019

Я написал вычислительно-интенсивную функцию (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 я понял, что она дает идентичный результат сотни раз для сеанса конечного пользователя, что на самом деле является пустой тратой ресурсов.

Естественно, я был склонен рассмотреть памятку , но не хотел увеличивать использование памяти в течение всей жизни программы; для всех пользовательских сессий в кеше может быть много уникальных данных, и данные из одной пользовательской сессии в любом случае недопустимы для другой пользовательской сессии. Параметр '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 МБ, что может быть GCed, как только сеанс закончится.

Ответы [ 2 ]

0 голосов
/ 16 января 2019

В ответ на (1) ваша функция get-cand выглядит очень похожей по структуре на использование из clojure.core/cache. Вы можете посмотреть в кеши FIFO / LIFO / TTL, которые также могут быть просто обернуты в атом для обновления.

Что касается (2), хотя я не думаю, что это фактически считается «мутацией» (может быть, эксперт сможет уточнить), я считаю, что вполне разумно иметь большие кэшированные предварительные вычисления (в конце концов Таблица поиска s распространены, например, при обработке изображений, и они в основном являются предварительно гидратированными кешами).

0 голосов
/ 16 января 2019

Я бы сделал это так, как вы предлагаете. Для этого не нужно использовать dosync & alter с ref. Просто используйте локальный atom в каждом model. Это может быть GC'd, когда модель больше не используется.


Обновление

Одной из альтернатив Java является использование LinkedHashMap. Вы можете установить максимальный размер и переопределить функцию removeEldestEntry() для управления поведением.


Обратите внимание, что этот синтаксис немного отключен, но я уверен, что вы знаете, как это исправить:

((deref...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...