c++ - 缓存优化和多态性

标签 c++ optimization c++11 polymorphism shared-ptr

假设我有一个使用 std::function 实现策略模式的计算器类对象如下(参见 Scott Meyers,Effective C++:改进程序和设计的 55 种具体方法):

class Calculator
{
public:
  ...
  std::vector<double> Calculate(double x, double y)
  {
     std::vector<double> res;
     for(const Function& f : functions)
        res.push_back(f(x,y));
     return res;
  }

private:
  std::vector<Function> functions;
};

在哪里

typedef std::function<double(double,double)> Function;

这是我面临的问题:假设函数 fg , 都是 Function 类型,在内部执行昂贵且相同的计算以获得最终结果。为了提高效率,可以将所有公共(public)数据包装在一个 struct 中。 ,计算一次并作为参数提供给他们。然而,这种设计有几个缺陷。例如,这会导致 Function 的签名发生变化。 ,这可能导致将不必要的参数传递给某些函数实现。此外,这些公共(public)和内部数据不再对代码中的其他组件隐藏,这可能会损害代码的简洁性。

我想讨论以下优化策略:实现一个类 CacheFG那:

  1. 定义一个Update用给定的 double 计算其内部数据的方法 xy ;和
  2. 定义一个Check确定其当前内部数据是否使用给定 double 计算的方法 xy .

然后我们可以做的就是制作fg共享类的公共(public)实例 CacheFG ,这可以使用 std::shared_ptr 来完成构造。因此,下面将创建 fg使用辅助函数的函数 f_auxg_aux .

double f_aux(double x, double y, const std::shared_ptr<CacheFG>& cache)
{
   if(not cache->Check(x,y))
      cache->Update(x,y);
   ...
}

std::shared_ptr<CacheFG> cache;
Function f = std::bind(f_aux, _1, _2, cache);
Function g = std::bind(g_aux, _1, _2, cache);

我的问题是:(1) 这是一种安全的优化方法吗? (2) 有没有更好的方法来解决这个问题?

编辑:经过几个回答后,我发现我的目的是在 C++ 中实现一个内存技术。我注意到只有最后计算的状态就足以满足我的目的。


感谢 DeadMG,我现在将在这里写下对他的方法的改进。他的想法包括使用内存技术 variadic templates .我只是提供了一个细微的修改,我在其中使用了结构 std::decay<Args>::type确保定义一个tuple仅适用于非引用类型。否则,带有 const-reference 参数的函数会导致编译错误。

template<typename Ret, typename... Args>
std::function<Ret(Args...)> MemoizeLast(std::function<Ret(Args...)> f)
{
    std::tuple<typename std::decay<Args>::type...> cache;
    Ret result = Ret();
    return [=](Args... args) mutable -> Ret
    {
        if(std::tie(args...) == cache)
            return Ret(result);
        cache = std::make_tuple(args...);
        return result = f(args...);
    };
}

为了防止result的移动, 当提供 return Ret(result) 时返回它的拷贝 ( args )是缓存的。

最佳答案

为什么要创建自己的类?您无需重新创建unordered_map 的界面。此功能可以添加为基于 std::functionstd::unordered_map 的可重用算法。我已经有一段时间没有使用可变参数模板了,但我希望你能理解。

template<typename Ret, typename... Args> 
std::function<Ret(Args...)> memoize(std::function<Ret(Args...)> t) {
    std::unordered_map<std::tuple<Args...>, Ret> cache;
    return [=](Args... a) mutable -> Ret {
        if (cache.find(std::make_tuple(a...)) != cache.end())
            return cache[std::make_tuple(a...)];
        else
            return cache[std::make_tuple(a...)] = t(a...);
    };
}

我不记得 std::hash 是否原生支持元组。如果没有,您可能需要添加它,或使用原生支持它们的 std::map

编辑:嗯,我没注意到你想要共享缓存。好吧,这应该不是一个太难的问题,只需在计算器中粘贴一个 unordered_map 成员并通过引用传递它,但这样做的语义似乎有点……奇怪。

再次编辑:只是最近的值?更简单。

template<typename Ret, typename... Args> 
std::function<Ret(Args...)> memoize_last(std::function<Ret(Args...)> t) {
    std::tuple<Args...> cache;
    Ret result;
    return [=](Args... a) mutable -> Ret {
        if (std::tie(a...) == cache)
            return result;
        cache = std::make_tuple(a...);
        return result = t(a...);
    };
}

如果你想在几个函数之间共享,那么改变是一样的 - 只需在类中声明它并作为引用传递。

关于c++ - 缓存优化和多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13066756/

相关文章:

c++ - opencv的 "findContours"错误: Thread stopped with code -1073740777

c++ - 在实现 operator[] 时,我应该如何包括边界检查?

javascript - 这样使用jQuery会不会减慢页面显示次数?

javascript - 浏览器加载内容 onScroll 多少算多了?

php - preg_match() 和 preg_replace() 慢吗?

c++ - boost ,以分钟为单位获得UTC时间

java - 什么是 Erlang 替代品?

c++ - 我不能将 lambda 作为 std::function 传递

c++ - 在模板化模板参数上专门化模板类构造函数

c++ - 拥有带有一些数据的 stringstream 如何以跨平台方式对其进行 gzip ?