我有几个简单的“表达式”类派生自
namespace Expression {
class Virtual {
public:
Virtual();
~Virtual();
virtual
double eval(double x, double y, double z) const = 0;
}
}
例如
class MyFun: public Virtual {
public:
MyFun();
~MyFun();
virtual
double eval(double x, double y, double z) const
{
return sin(x*x) + exp(z)*y + 3.0; // some expensive expression
}
}
这些表达式在eval
中很多,但是非常具体的xyz
,即网格的节点
class MyMesh {
MyMesh();
~MyMesh();
virtual
std::vector<std::vector<double> > getNodes() const
{
return allMyNodes_;
}
}
被调用者将实例化一个网格,实例化Expression
,然后继续进行一些数值过程,其中可能多次在网格节点中评估Expression
。
MyMesh myMesh();
MyFun myExpr();
Result result = numericalProcedure(myMesh, myExpr);
// [...]
由于eval
很贵,我想到了如何加快这一点。我想到的是缓存。
一个想法是将所有eval
结果存储在带有索引的向量中的所有网格节点中,我想知道如何最好地实现它。我不想在Expression
中粘贴任何网格数据(以保持缓存),我也不想让接口的接口更复杂。
是否存在与此用例匹配的缓存设计模式?什么是将缓存逻辑与其余代码隔离开来的方法?
答案 0 :(得分:1)
您可以将参数数组中的地图存储到仿函数中的结果中:
class MyFun {
public:
virtual
double eval(double x, double y, double z) const
{
std::array<double,3> arr {x,y,z};
//is the answer cached?
if (cache_.count(arr))
{
return cache_[arr];
}
double ret = sin(x*x) + exp(z)*y + 3.0;
//cache the result
cache_[arr] = ret;
return ret;
}
private:
//need mutable so it can be modified by a const function
mutable std::map<std::array<double,3>, double> cache_;
};
您甚至可以在基类中进行缓存,然后将评估转发给虚函数:
class BaseFun {
public:
double eval(double x, double y, double z) const
{
std::array<double,3> arr {x,y,z};
if (cache_.count(arr))
{
return cache_[arr];
}
double ret = doEval(x,y,z);
cache_[arr] = ret;
return ret;
}
protected:
virtual double doEval (double x, double y, double z) const = 0;
private:
mutable std::map<std::array<double,3>, double> cache_;
};
class MyFun : public BaseFun {
private:
virtual double doEval (double x, double y, double z) const override
{
return sin(x*x) + exp(z)*y + 3.0;
}
};
答案 1 :(得分:1)
我会实现一个新的Expression::Virtual
子类,它只进行缓存,同时保留另一个表达式的实例来委托实际的计算:
class CachedExpression: public Expression::Virtual {
private:
// for simplicity I assume a separate Point class
mutable std::map<Point, double> cache_;
const Expression::Virtual* expr_; // or better auto_ptr or friends
public:
explicit CachedExpression(const Expression::Virtual* expr): cache_(), expr_(expr) {}
virtual double eval(const Point& point) const {
if (cache_.find(point) == cache_.end())
cache_[point] = expr_.eval(point);
return cache_[point];
}
};
...
MyMesh myMesh;
MyFun myExpr;
CachedExpression myCached(&myExpr);
// or even CachedExpression myCached(new myFun());
Result result = numericalProcedure(myMesh, myCached);
通过这种方式,您始终可以仅使用myExpr
而不是myCached
来关闭缓存,或者在不同的{{保留仅N
最后查询以保存内存)中实施不同的缓存策略{1}} - 类似课程,并根据您的需要使用。
答案 2 :(得分:1)
将缓存逻辑与其余代码隔离的方法是什么?
您可以使用相同的接口将缓存分成另一个类,这可以缓存任何表达式的结果:
class Cache : public Virtual {
public:
explicit Cache(Virtual const & target) : target(target) {}
double eval(double x, double y, double z) const override {
std::array<double,3> key {x,y,z};
auto cached = cache.find(key);
if (cached == cache.end()) {
double result = target(x,y,z);
cache[key] = result;
return result;
} else {
return cached->second;
}
}
private:
Virtual const & target;
std::map<std::array<double,3>, double> cache;
};
可以这样使用
Result result = numericalProcedure(myMesh, Cache(myExpr));
在现有表达式周围包装临时缓存。
(如果你想使用一个更永久的缓存,那么它应该有一个失效策略来阻止它变得太大。我的简单例子只在破坏时释放内存,所以如果它永远不会成为内存泄漏破坏。)
是否存在与此用例匹配的缓存设计模式?
如果您想为其命名,那就是Proxy pattern的一个例子。