2024 Clojure状态调查 中分享您的想法!

欢迎!有关如何工作的更多信息,请查看关于页面。

0
core.memoize

问题:函数A已memo化。函数B(以及其他一些功能),计算A的有效函数调用,但从不调用A。我们希望利用这种效果在B中进一步减少A的计算。如果我们预期B的运行速度比A快,我们还想让调用A的用户在相应的B运行时等待。

B中如何实现所需操作的示例
(spoof A args calc)

其中calc是一个零参数函数,其生成的值等同于(apply A args)。这允许调用者将投入calc中的工作量精确地控制在比A运行得更快。

Spoof返回calc返回的值,或者在缓存命中时返回args的缓存值。也就是说,如果之前发生了(A args)的调用,则不会调用calc。

在已经拥有(A args)的结果且不想被(A args)的并发调用阻塞的情况下,希望的操作是

(spoof-unsynchronized A args val)

它立即返回val并在不久之后更新A的缓存。由于无同步spoof是一种非常快的操作,它不可避免地与(A args)的调用存在竞争条件,因此无需阻塞(A args)的调用。(因此,swap!对内部缓存状态足够了)。

11 答案

0

评论者:seancorfield

这感觉不像应该是core.memoize库的一部分,但应该可以在其之上构建类似的东西(并且现在还不是,对吗?)

为了允许这样做,所需的最小更改是否需要公开底层缓存(因此可以直接对缓存调用deref/swap!)?

0

评论者:lgs32a

对于非同步情况,是的;对于同步(有趣)的情况,可能需要通过篡改才能实现,我需要查看代码。

我不同意这个功能不应该内置。这个功能非常通用,允许非常灵活的用户自定义缓存实现。

以上推荐的设计是借鉴自Google Guavas的缓存。

它允许这样的缓存。你只需要调用
(.get c (link: arg1] (fn []) "为(link: arg1)计算")) ;; 注意如何通过这样一个功能轻松地实现自定义缓存的实现!

在核心.memoize中实现运行fn同步逻辑的必要逻辑。所以,如果core.memoize提供了类似的设施,我相信这将是一个非常通用的API,允许构建更多东西(自定义缓存实现)。

它还有一个.put处理非同步情况。

0

评论者:seancorfield

很有趣。我的下一个问题将是“你是从哪里得到这个想法的?”

{{core.memoize}}的底层机器以类似的方式工作,有一个* }函数处理thunk等,但是是{{build-memoizer}}函数封装了所有这些,以及{{PluggableMemoizer}}类型。

我需要好好思考这个问题。缓存是附加到从{{build-memoizer}}返回的函数对象的元数据上的,所以,如果需要自己编写程序,技术上已经全部暴露出来了。

0

评论者:lgs32a

我的理解是,我需要通过带修复的(忽略参数)fn.*来调用。

真的不能称之为公开:)

0

评论者:lgs32a

我还需要在209行之后的代码中进行复制粘贴处理,以解决一个罕见的时间问题。

0
by

评论者:lgs32a

还是我理解错了,你建议我编写一个补丁吗?

0
by

评论者:seancorfield

我的意思是:如果你想要检查是否存在某个参数列表键或者通过你上面提到的thunk更新缓存的新值,原始缓存就存在于元数据中。你可以直接在那个缓存上调用{{core.cache/through}}(或更有可能是{{core.cache/through-cache}}),使用一个thunk(尽管只有一个参数,你可以直接忽略)

(swap! (:clojure.core.memoize/cache (meta the-fn-var)) clojure.core.cache/through_cache [arg1] (fn [_] "为[arg1]计算"))

任何使用TTL缓存的用户都必须考虑到在访问过程中可能出现的异常情况(除非他们不介意返回{{nil}}),因此,是的,一个执行上述swap!的{{spoof}}函数可能需要重新尝试thunk调用 -- 是否在那个时刻更新缓存将取决于你(即,如果你在{{spoof}}调用期间发生缓存驱逐,你希望后续的调用有什么行为?)

关键是{{spoof}}需要实际缓存来完成其工作。缓存的函数仅通过闭包的方式访问该缓存,所以{{spoof}}必须操作于持有缓存的{{Var}}。

这解释清楚我所建议的吗?

0
by

评论者:lgs32a

{{spoof}}还将利用d-lay中的内部through*锁定功能。它可能需要一些调整以支持thunk。

上述推荐的设计故意采用了缓存的函数,以便提取::cache。

关于TTL修复,我需要再次查看。如果你对带有spoof/spoof-aysnc的补丁感兴趣,请告诉我。

0
by

评论者:seancorfield

名字我不喜欢,但如果补丁可以暴露足够的机制,使用户可以更轻松地在库外部构建自己的版本,我会考虑。

0

评论者:lgs32a

我选择的名字是为了适应lib的当前设计。WHERE Guava为你提供了强大的基本功能并声明了通用的底层名称,这个lib是按照这样的风格编写的,即它对用户想要如何使用它作出了强烈的假设。它让你立即拥有一个带有缓存的备忘函数。

这就是为什么我只选择描述 why you would use them /additionally/ 的名字。可能比“spoof”更好的词——我不是母语者。

core.cache仅提供了缓存的 数据结构,但它并没有暴露缓存计算的合适引用类型(而不是值),就像Guava那样。我认为core.cache的作者之所以没有这样做,是因为他们认为Clojure的引用类型就足够了。core.memoize为自身实现了这样的引用类型作为内部。我不确定是否应该在lib中公开这种功能,因为它有很多用途场景,而不仅仅是备忘录化。因此命名和包装备忘函数,以清楚地表明功能应该在备忘的范围之内。

0
参考: https://clojure.atlassian.net/browse/CMEMOIZE-24 (由lgs32a报告)
...