[英]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;
這是我面臨的問題:假設函數f
和g
都是Function
類型,在內部執行昂貴且相同的計算以獲得最終結果。 為了提高效率,可以將所有公共數據包裝在struct
,計算一次並作為參數提供給它們。 但是,這種設計有幾個缺陷。 例如,這會導致Function
的簽名發生更改,這可能導致將不必要的參數傳遞給某些函數實現。 而且,這些公共和內部數據不再隱藏在代碼中的其他組件中,這可能會損害代碼的簡單性。
我想討論以下優化策略:實現一個類CacheFG
:
Update
方法,用一對給定的雙精度x
和y
計算其內部數據; 和 Check
方法以確定其當前內部數據是否使用給定的一對雙x
和y
。 那么我們可以做的是使f
和g
共享一個CacheFG
類的公共實例,這可以使用std::shared_ptr
結構來完成。 因此,下面將使用輔助函數f_aux
和g_aux
創建f
和g
函數。
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::function
和std::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.