缓存重复输入数据的表达式求值
Caching of expression evaluations for repeated input data
我有一对从
派生的简单的"表达式"类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
}
}
这些表达式在许多但非常具体的xyz
中是eval
ed,即网格的节点
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
s中粘贴任何网格数据(以保持缓存),也不想为被调用者制作更复杂的接口。
是否存在与此用例匹配的缓存设计模式?将缓存逻辑与其余代码隔离开来的方法是什么?
可以在函函数中存储从实参数组到结果的映射:
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;
}
};
将缓存逻辑与其他代码隔离的方法是什么?
可以将缓存分离到具有相同接口的另一个类中,该类可以缓存任何表达式的结果:
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));
将临时缓存封装在现有表达式的周围。
(如果你想使用一个更永久的缓存,那么它应该有一个失效策略来阻止它变得太大。我的简单示例仅在销毁时释放内存,因此如果它从未销毁,则可能成为内存泄漏。
是否存在与此用例匹配的缓存设计模式?
如果你想给它命名,那就是代理模式的一个例子。
我将实现一个新的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
来关闭缓存,或者在不同的CachedExpression
类中实现不同的缓存策略(例如仅保留N
最后查询以节省内存),并使用您需要的任何缓存。
相关文章:
- 防止主数据类型C++的隐式转换
- 用于访问容器<T>数据成员的正确 API
- (C++)分析树以计算返回错误值的简单算术表达式
- 嵌套在类中时无法设置成员数据
- 使用流处理接收到的数据
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 如何确定涉及 C++ 中除法的算术表达式的数据类型
- 用于 ARP 回复数据包的 c/c++ pcap 过滤器表达式
- 无法从数组二次表达式中检索数据值
- 使用正则表达式从文件中读取数据信息
- 使用折叠表达式初始化静态 constexpr 类数据成员不编译
- c++ 如何计算涉及双精度数据类型模数的数学表达式
- 用于分析数据的正则表达式
- 在C++03中,在未赋值的上下文中使用表示非静态数据成员的id表达式有效吗
- C,C++,数据类型,运算符和表达式
- 使用正则表达式从C++中的一行文本中提取数据
- 如何在lambda表达式中捕获单个类数据成员
- 用new表达式初始化非静态数据成员
- 缓存重复输入数据的表达式求值