caching失效 – 是否有一个通用解决scheme?

“计算机科学只有两个难题:caching失效和命名事情。”

菲尔·卡尔顿

是否有一个通用的解决scheme或方法来使caching无效; 要知道什么时候一个条目陈旧,所以你保证总是得到新的数据?

例如,考虑从文件获取数据的函数getData() 。 它基于文件的最后一次修改时间对其进行caching,每次调用它时都会进行检查。
然后添加第二个函数transformData()来转换数据,并在下次调用函数时caching结果。 它没有文件的知识 – 你如何添加依赖,如果文件被改变,这个caching变得无效?

每次调用transformData()都可以调用getData() ,并将其与用于构buildcaching的值进行比较,但这可能会导致代价非常高昂。

你所说的是终身依赖链,一件事依赖于另一件事,它可以在其控制之外进行修改。

如果从abc有一个幂等函数,如果ab是相同的,那么c是相同的,但是检查b的代价是高的,那么你可以:

  1. 接受你有时运行过时的信息,不要总是检查b
  2. 尽可能快地进行检查b

你不能吃你的蛋糕,吃…

如果你可以根据上面a层叠一个额外的caching,那么这会影响最初的问题,而不是一个位。 如果你select了1,那么你有自己的任何自由,因此可以caching更多,但必须记住要考虑b的caching值的有效性。 如果你select了2,那么你每次都必须检查b但如果b检出,可以退回到caching。

如果对caching进行分层,则必须考虑是否由于组合行为而违反了系统的“规则”。

如果你知道如果ba总是有效的,那么你可以像这样安排你的caching(伪代码):

 private map<b,map<a,c>> cache // private func realFunction // (a,b) -> c get(a, b) { c result; map<a,c> endCache; if (cache[b] expired or not present) { remove all b -> * entries in cache; endCache = new map<a,c>(); add to cache b -> endCache; } else { endCache = cache[b]; } if (endCache[a] not present) // important line { result = realFunction(a,b); endCache[a] = result; } else { result = endCache[a]; } return result; } 

显然,连续分层(如x )是微不足道的,只要在每个阶段新添加的input的有效性与xbxaab关系匹配。

但是,很有可能你会得到三个有效性完全独立的(或者是循环的)input,所以不可能分层。 这意味着标记为重要的行将不得不改变

如果(endCache [a] 过期或不存在)

在caching失效的问题是,东西改变,我们不知道它。 所以,在某些情况下,如果有其他的东西知道它并且可以通知我们,那么解决scheme是可能的。 在给定的示例中,getData函数可以挂接到文件系统,该文件系统知道文件的所有更改,而不pipe文件的哪个进程发生更改,并且此组件可以通知转换数据的组件。

我不认为有任何一般的魔术解决scheme可以让问题消失。 但是在很多实际情况下,很可能有机会将基于“投票”的方法转变为基于“中断”的方法,从而使问题简单地消失。

如果您每次进行转换都要获取Data(),那么您已经消除了caching的全部好处。

对于你的例子,看起来像是一个解决scheme将是当你生成转换的数据,也存储文件的文件名和最后修改时间的文件(你已经存储这个数据结构是由getData( ),因此只需将该logging复制到transformData()返回的数据结构中,然后再次调用transformData()时,检查文件的上次修改时间。

我现在正在基于PostSharp和memoizing函数开发一种方法。 我已经跑过了我的导师,他同意这是一个很好的内容无关的caching实现。

每个function都可以用一个指定其有效期限的属性来标记。 以这种方式标记的每个函数都被记忆,并且结果被存储到caching中,函数调用和参数被用作关键字的散列。 我使用Velocity作为后端,处理caching数据的分发。

恕我直任,function反应编程(FRP)在某种意义上是解决caching失效的一般方法。

这是为什么:FRP术语中陈旧的数据被称为毛刺 。 FRP的目标之一是保证没有毛刺。

在这个“FRP的本质”谈话和在这个答案中更详细地解释了FRP 。

在讲话中 , Cell代表一个caching的对象/实体,如果它的一个依赖关系被刷新,则一个Cell被刷新。

FRP隐藏了与依赖图关联的pipe道代码,并确保没有陈旧的Cell

是否有一个通用的解决scheme或方法来创build一个caching,要知道什么时候一个条目陈旧,所以你保证总是得到新的数据?

不,因为所有的数据都不一样 一些数据可能在一分钟之后“过时”,一些在一小时之后,有些数据可能会在几天或几个月内被罚款。

关于你的具体例子,最简单的解决scheme是从getDatatransformData调用的文件的“caching检查”function。

没有通用的解决scheme,但是:

  • 您的caching可以充当代理(拉)。 假设你的caching知道最后一个原点改变的时间戳,当有人调用getData() ,caching询问原点是否是最后一次改变的时间戳,如果相同,则返回caching,否则用源头更新其内容并返回其内容。 (变化是客户端直接发送请求的时间戳,源只会返回时间戳不同的内容。)

  • 您仍然可以使用通知进程(推送),caching观察源,如果源更改,它会发送一个通知caching,然后标记为“脏”。 如果有人调用getData() ,caching将首先更新到源,删除“脏”标志; 然后返回其内容。

一般来说,select取决于:

  • 频率:在getData()上的很多调用会喜欢推送,以避免源被getTimestamp函数淹没
  • 您访问来源:您是否拥有源模型? 如果不是,则可能无法添加任何通知过程。

注意:由于使用时间戳是传统的http代理方式正在工作,另一种方法是共享存储内容的散列。 我知道两个实体一起更新的唯一方法是或者我打电话给你(拉),或者你打电话给我…(推)这一切。

也许cache-obliviousalgorithm将是最普通的(或者至less,更less的硬件configuration依赖),因为他们将首先使用最快的caching并从那里继续。 这是一个麻省理工学院的讲座: caching不经意的algorithm

Interesting Posts