gpt4 book ai didi

caching - 如何在 elisp 中实现过期 LRU 缓存?

转载 作者:行者123 更新时间:2023-12-02 19:03:31 26 4
gpt4 key购买 nike

我的数据由以下三个部分组成:

  • a_path
  • a_key
  • a_value =f(a_path, a_key)

a_value 的计算成本很高,因此我不想经常计算它。在理想的世界中,只有当情况发生变化时才会发生这种情况。所以,我对这个缓存的要求如下:

  • 具有可配置最大大小的 LRU 缓存
  • 键入 (a_path, a_key)
  • 能够根据年龄使条目过期(例如,每小时左右重新计算一次)
  • 能够根据expiry_func使条目过期(a_path, a_key)

我的谷歌搜索失败了;即使在搜索“elisp LRU 缓存”时,我也发现了很多 Java 站点。

最佳答案

这就是您想要的大部分内容:固定大小的最近最少使用的缓存,具有 O(1) 查找、O(1) 插入和 O(1) 删除。

让所有这些操作都变成 O(1) 有点棘手,因此这个实现稍微复杂一些。我将哈希表(用于快速查找)与双向链接的项目列表(用于快速删除、重新排序和查找最旧的元素)结合起来。

(require 'cl)

(defstruct lru-cache max-size size newest oldest table)

(defstruct lru-item key value next prev)

(defun lru-remove-item (item lru)
(let ((next (lru-item-next item))
(prev (lru-item-prev item)))
(if next (setf (lru-item-prev next) prev)
(setf (lru-cache-newest lru) prev))
(if prev (setf (lru-item-next prev) next)
(setf (lru-cache-oldest lru) next))))

(defun lru-insert-item (item lru)
(let ((newest (lru-cache-newest lru)))
(setf (lru-item-next item) nil (lru-item-prev item) newest)
(if newest (setf (lru-item-next newest) item)
(setf (lru-cache-oldest lru) item))
(setf (lru-cache-newest lru) item)))

;;; Public interface starts here.

(defun* lru-create (&key (size 65) (test 'eql))
"Create a new least-recently-used cache and return it.
Takes keyword arguments
:SIZE the maximum number of entries (default: 65).
:TEST a hash table test (default 'EQL)."
(make-lru-cache
:max-size size
:size 0
:newest nil
:oldest nil
:table (make-hash-table :size size :test test)))

(defun lru-get (key lru &optional default)
"Look up KEY in least-recently-used cache LRU and return
its associated value.
If KEY is not found, return DEFAULT which defaults to nil."
(let ((item (gethash key (lru-cache-table lru))))
(if item
(progn
(lru-remove-item item lru)
(lru-insert-item item lru)
(lru-item-value item))
default)))

(defun lru-rem (key lru)
"Remove KEY from least-recently-used cache LRU."
(let ((item (gethash key (lru-cache-table lru))))
(when item
(remhash (lru-item-key item) (lru-cache-table lru))
(lru-remove-item item lru)
(decf (lru-cache-size lru)))))

(defun lru-put (key value lru)
"Associate KEY with VALUE in least-recently-used cache LRU.
If KEY is already present in LRU, replace its current value with VALUE."
(let ((item (gethash key (lru-cache-table lru))))
(if item
(setf (lru-item-value item) value)
(when (eql (lru-cache-size lru) (lru-cache-max-size lru))
(lru-rem (lru-item-key (lru-cache-oldest lru)) lru))
(let ((newitem (make-lru-item :key key :value value)))
(lru-insert-item newitem lru)
(puthash key newitem (lru-cache-table lru))
(incf (lru-cache-size lru))))))

;;; Exercise for the reader: implement lru-clr and lru-map to complete the
;;; analogy with hash tables.

对于以对为键的应用程序,您可能需要向 lru-create 提供 :test 'equal。或参见Defining Hash Comparisons如果您需要一些特别的东西。

我会让你弄清楚如何进行基于时间的到期;从这里开始应该很简单。

(如果有人知道一种更简单的方法来实现这一点,同时保持操作在恒定时间内运行,我将非常有兴趣看到它。)

关于caching - 如何在 elisp 中实现过期 LRU 缓存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6652956/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com