2015-07-02 4 views
3

Я хотел бы улучшить производительность функции, которая возвращает измененные изображения. Запрашиваемый размер изображений не должен сильно отличаться (зависит от устройства), поэтому имеет смысл как-то кэшировать результаты.Clojure - memoize на диске

Я мог бы, конечно, сохранить его на диске, и проверить, если новое изображение существует, и убедитесь, что если исходное изображение будет удалено, змененные версии тоже ...

Или я мог бы использовать memoized функция. Но так как результат потенциально довольно большой (изображение составляет около 5 - 10 МБ, я думаю), бессмысленно хранить их в памяти (несколько десятков ГБ изображений и их модифицированные версии будут заполнять память довольно быстро).

Итак, есть ли способ иметь memoized функцию, которая действует как обычный Clojure defmemo, но поддерживается ли папкой на локальном диске вместо памяти? Затем я мог бы использовать стратегию ttl, чтобы убедиться, что изображения не слишком долго не синхронизируются.

Нечто похожее на crache, но при поддержке файловой системы?

+0

Вы можете сохранить измененные изображения в Redis или Memcached с некоторым разумным сроком действия для автоматической очистки неиспользуемых записей. –

+0

Просто посмотрел на вашу ссылку «crache», и она отлично подходит для вашей задачи. –

+0

@LeonidBeschastny Well crache поддерживается Redis, который является хранилищем данных в памяти. К сожалению, у меня не так много RAM, поэтому я стараюсь избегать этого и вместо этого имею файловую систему. – nha

ответ

3

Не переусердствуйте. Ваша файловая система как кеш - правильная идея. Если один файл становится популярным, и к файлу обращается много, то ваша операционная система будет следить за тем, чтобы он находился в ОЗУ. Это та же стратегия, что и многие базы данных. Например, Elasticsearch требует, чтобы вы оставили достаточное количество ОЗУ, чтобы иметь файлы индекса Lucene в ОЗУ.

Не изменяйте ваши файлы ни когда! Сделайте это по-функциональному: обрабатывайте их как неизменные данные. Ваш входной файл не должен изменяться. Если это так, то это новый файл. Пространство на жестком диске является недешево дешевым. Не бойтесь иметь много файлов, лежащих вокруг. Если вам нужно, вы можете сделать сборку мусора, которая через некоторое время удаляет старые/помеченные файлы.

Чтобы проверить, есть ли файл в кеше, вы просто проверяете, существует ли файл. Если это не так: вы пишете его один раз.

Итак, подведем итог:

  • Пусть ваш O/S работает кэширование
  • Не редактировать файлы. Рассматривайте их как непреложные данные. Запись раз
  • Ваш O/S освободит ОЗУ неиспользуемых файлов. Место на жестком диске супер дешево.
+0

Не могли бы вы расширить немного на «неизменяемых файлах»? – nha

+0

Файл изображения является файлом изображения и не должен мутировать. Измененный файл всегда будет измененным размером файла входного изображения. Если ваш алгоритм изменения размера не изменится, нет причин переписывать его. Нет ничего плохого в том, что на диске имеется несколько разных размеров. Соглашение об именах для измерения, вероятно, проще всего. Filename == cache key – ClojureMostly

+0

Правильно, это план (я даже не думал в терминах неизменяемости, поскольку их можно удалить), но это будет достигнуто с помощью последовательной схемы именования. +1 :) В итоге я реализую core.cache с файловой системой. – nha

1

Что вам нужно, это отличное использование для Datomic. Он легко используется из Clojure, достаточно эффективен и, как и любая хорошая БД, он имеет кэш-память с наименее используемым (LRU) в памяти. В Postgres, Redis, DynamoDB, Riak и т. Д. Он также может использовать широкий спектр баз данных баз данных в качестве субстрата: от строго встроенной памяти (лучше всего для тестирования экспериментов &). Существует также режим «dev», который использует локальные файлы для все хранение.

Смотреть все подробности здесь:

Существует стандартная версия с бесплатной бессрочной лицензией, которая подходит для большинства применений. Для расширенных функций доступна платная версия.

+0

Хорошо, что бы добавить немного оперативных издержек, не так ли? И исправьте меня, если я ошибаюсь, но кеш - это кеш в памяти, чего я пытаюсь избежать? – nha

+0

Накладные расходы будут очень маленькими. Datomic использует базу данных поддержки, такую ​​как Postgres, которая будет хранить большую часть данных на диске. Только недавно использованные элементы будут в памяти. –

+0

Ну, но теперь мне нужно развернуть Datomic + Postgres .. Удивительные технологии, но это звучит как перебор для хранения изображений для меня. – nha

2

Почему бы не реализовать TTL-кеш из clojure.core.cache, не обернув его необходимой функциональностью? Ваш ключ может быть любым, который идентифицирует ваше измененное изображение, и значение будет его местоположением на диске. Тогда вы могли бы реализовать какой-то набор или набор! функция, передавая ему функцию, которая будет вызываться для генерации изображения, когда оно не существует. , например.

(def Cache (atom (cache/ttl-cache-factory {} :ttl 20000))) 

(defn get-or-update! 
    "wraps the recommended has-hit-get pattern 
    https://github.com/clojure/core.cache/wiki/Using" 
    [key fn] 
    (if (cache/has? @Cache key) 
    (get (swap! Cache #(cache/hit % key)) key) 
    (get (swap! Cache #(cache/miss % key (fn))) key))) 
+0

+1 хороший сниппет. Ну, это была более или менее оригинальная идея (я несколько удивлен, что никто этого не сделал, но это должно быть достаточно простым для реализации). Теперь информация 'ttl' будет постоянной после перезагрузки или обновления сервера? Это не имеет значения с атомом, но с постоянными данными на диске. – nha

+1

Я обновил фрагмент. Нет, и в этом случае вам, вероятно, также захочется написать функцию, которая генерирует начальное семя для кеша из существующих изображений на диске. Если сохранение информации 'ttl' важно с помощью перезагрузки, оно начинает немного усложняться, и вам, возможно, придется искать расширение core.cache или перейти к другому решению. – Nick

+2

Эта реализация неверна. Состояние вашего атома кэша может изменяться между вызовами 'cache/has' и' cache/hit' или 'cache/miss'. Ваша реализация должна выглядеть так: '(get (swap! Cache # (if (cache/has?% Key) (cache/hit% key) (cache/miss% (fn) key))))' – Palesz

Смежные вопросы