C++ C++;函数输出的简单缓存设计
我想这是一个非常常见的问题,与众所周知的解决方案,而我不是 能够找到。所以我在这里寻求建议 问题陈述 考虑以下设置:C++ C++;函数输出的简单缓存设计,c++,caching,C++,Caching,我想这是一个非常常见的问题,与众所周知的解决方案,而我不是 能够找到。所以我在这里寻求建议 问题陈述 考虑以下设置: class A; // some class const A f(const A&); // an _expensive_ function void do_stuff() { A a; a.modify(...); do_stuff1(f(a)); // compute f(a) do_stuff2(f(a)); // use
class A; // some class
const A f(const A&); // an _expensive_ function
void do_stuff()
{
A a;
a.modify(...);
do_stuff1(f(a)); // compute f(a)
do_stuff2(f(a)); // use cached value of f(a)
a.modify(...);
do_stuff3(f(a)); // recompute f(a)
}
我希望f(a)
的返回值在第一个和第二个
第二次调用,但在第二次调用a.modify()
后将被丢弃。
EDIT:实际上,对f(a)
的调用将在不同的范围内
以下是我已经探索过的解决方案,值得一试
解决方案1:中央缓存
使用时间戳
我可以想象一个简单的解决方案,包括在类a
中添加一个时间戳
函数f
可以检查并决定是否需要更新其缓存结果,
存储在中央缓存的某个地方。我想这也意味着改变
f
签名人:
const A& f(const A&);
问题1:对于中央缓存,我们需要一种机制来销毁
销毁a
时f(a)
的缓存结果
使用散列码
除了问题1,这似乎很简单。但是当A
表示std::vector。我想应该排除动态多态性
在这里所以我们忘了在std::vector
的子类中添加时间戳
这意味着什么。但是,我们可以计算一些哈希代码或UUID
基于a
的内容——假设它比计算便宜得多
f(a)
——并将中央缓存基于这些散列码。但我们面临的是
问题1
解决方案2:耦合对象
我还没有找到如何实现这一点,但想法是让a
notify
当a
被写入或销毁时f(a)
的缓存,但当它被
仅仅是阅读。如果没有动态多态性,我想不出怎么做,
使用操作符[]
或
迭代器,为每个修改的元素向缓存发送通知
问题2:找到一种机制,对a
的更改集进行定界,以便对每组更改仅使缓存失效一次
我曾想到代理可以在a
上启用写访问(受此概念启发)
但无法生成任何工作代码
有什么想法吗?你就不能这样做:
const A &cacheA = f(a);
do_stuff1(cacheA); // compute f(a)
do_stuff2(cacheA); // use cached value of f(a)
我在界面上也做过类似的工作,比如:
class F
{
public:
virtual int f(int a)=0;
};
class Cache : public F
{
public:
Cache(F &f) : f(f) { }
int f(int a) { /*caching logic here, calls f.f() if not found from cache */ }
F &f;
};
class Impl : public F
{
int f(int a) { /* real implementation here */ }
};
然后决定在何处使用缓存逻辑:
Impl i;
Cache c(i);
c.f(10); // put to cache with key 10
c.f(10); // found from cache
c.f(11); // put to cache with key 11
我可能在这里遗漏了一些重要的细节,但您不能仅为此目的使用a吗?将f作为a的成员。然后您可以在a的实例中决定是否可以重用缓存的结果。您不能存储
返回值并重用它吗?如果您可以更改f(常数A&)
,则可以将A
的临时对象作为第二个参数传递,并使用相同的参数。@Munger,您提到A
可以是任何东西,甚至std::vector
。A
是什么?这是理论问题还是实践问题?什么是A
准确地说?@dialuticus:对于手头的问题,它是一个std::vector
。但是我记得在其他类型的对象中也遇到了这个问题,所以我写了A
,也包括了这些情况。也许我不应该。比较两个向量比调用一次函数便宜得多吗?如果这两个操作的成本大致相同,那么就没有太多的优化空间。@Dialogus:是的,比较向量要比调用函数便宜得多。可能不是。所示代码是一个概念性的示例,对f(a)
的两个调用可能位于代码中完全不相关的部分。不,我上面给出的代码过于简化了。对f(a)的调用实际上发生在相互排斥的作用域中。@Munger:那么你应该编辑你的问题,并更清楚地说明调用将在不同的作用域中。我喜欢这样。但我还是要解决上面的问题1f
将在许多不同的对象上被调用,因此为所有已销毁的old_a
保留f(old_a)
的缓存值是个坏主意。@munger销毁缓存值的方式有两种:缓存对象超出范围/被销毁/替换,或者,您可以向缓存中添加一个成员函数,该函数允许您从缓存中显式删除一些缓存值或一系列值。缓存可以有您喜欢的任何成员函数,因此可以通过这种设计来实现这些功能。我使用boost::function的类似方法,通过调用可能多次调用该函数的缓存版本和其他地方的未缓存版本,成功地实现了所需的功能。在缓存映射中,我将需要大量存储的向量替换为SHA-1散列值。这主要是因为我已经知道缓存对象的最佳生存时间,即直到原始对象被销毁为止。因为在我所想到的特定应用程序中,大多数应用程序的生命周期都很短,所以LRU缓存将是内存的浪费。不过,这一点很好。在我想到的特定应用程序中,f(a)
只是a
的一种替代表示形式。所以,出于设计原因,你建议的是我想要走的路,以及我的第一个想法。这就提出了两个问题:(1)如何对没有虚拟成员的std::vector进行子类化,以及(2)何时设置对象的修改标志。