簡體   English   中英

緩存優化和多態性

[英]Cache Optimization and Polymorphism

假設我有一個使用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類型,在內部執行昂貴且相同的計算以獲得最終結果。 為了提高效率,可以將所有公共數據包裝在struct ,計算一次並作為參數提供給它們。 但是,這種設計有幾個缺陷。 例如,這會導致Function的簽名發生更改,這可能導致將不必要的參數傳遞給某些函數實現。 而且,這些公共和內部數據不再隱藏在代碼中的其他組件中,這可能會損害代碼的簡單性。

我想討論以下優化策略:實現一個類CacheFG

  1. 定義一個Update方法,用一對給定的雙精度xy計算其內部數據;
  2. 定義Check方法以確定其當前內部數據是否使用給定的一對雙xy

那么我們可以做的是使fg共享一個CacheFG類的公共實例,這可以使用std::shared_ptr結構來完成。 因此,下面將使用輔助函數f_auxg_aux創建fg函數。

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 ++中實現一個memoization技術 我注意到只有最后計算的狀態足以滿足我的目的。


感謝DeadMG ,我現在寫這里只是改進了他的方法。 他的想法包括使用具有可變參數模板的memoization技術。 我只是稍微修改一下,我使用構造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的移動,當提供的args是緩存的args時, return Ret(result)它的副本( return Ret(result) )。

為什么要創建自己的課程? 您無需重新創建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...);
    };
}

如果要在多個函數之間共享,則更改是相同的 - 只需在類中聲明它並作為引用傳入。

在優化之前 - 測量。 然后,如果您真的使用相同的值執行許多計算 - 則創建此緩存對象。 我想在CacheFG::get(x, y)隱藏緩存檢查和更新,並像const auto value = cache->get(x,y)一樣使用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM