我有一个函数,f(a,b)
它接受两个输入。我不知道将使用a
和的哪些值。b
我可以在内存上有点浪费(我关心速度)。我希望能够检查 的输出f(a,b)
是否已经交付,如果是,则再次交付该输出,而无需重新运行该f(a,b)
流程。
在 Python 中使用装饰器很容易做到这一点,但 C++ 在我这里是遥不可及的。
我有一个函数,f(a,b)
它接受两个输入。我不知道将使用a
和的哪些值。b
我可以在内存上有点浪费(我关心速度)。我希望能够检查 的输出f(a,b)
是否已经交付,如果是,则再次交付该输出,而无需重新运行该f(a,b)
流程。
在 Python 中使用装饰器很容易做到这一点,但 C++ 在我这里是遥不可及的。
海报问:
我希望能够检查 f(a,b) 的输出是否已经交付,如果是,则再次交付该输出,而无需重新运行 f(a,b) 流程。
在 C++ 中使用std::map
. 函数恰好有两个参数这一事实意味着我们可以std::pair
用来描述它们。
#include <map>
#include <iostream>
uint64_t real_f(int a, int b) {
std::cout << "*";
// Do something tough:
return (uint64_t)a*b;
}
uint64_t memo_f(int a, int b) {
typedef std::pair<int, int> key;
typedef std::map<key, uint64_t> map;
static map m;
key k(a,b);
map::iterator it = m.find(k);
if(it == m.end()) {
return m[k] = real_f(a, b);
}
return it->second;
}
int main () {
std::cout << memo_f(1, 2) << "\n";
std::cout << memo_f(3, 4) << "\n";
std::cout << memo_f(1, 2) << "\n";
std::cout << memo_f(3, 4) << "\n";
std::cout << memo_f(5, 6) << "\n";
}
上述程序的输出是:
*2
*12
2
12
*30
没有星号的行代表缓存的结果。
使用 C++11,您可以使用任务和期货。让f
成为你的功能:
int f(int a, int b)
{
// Do hard work.
}
然后你会安排函数执行,它会返回一个返回值的句柄。这个句柄被称为未来:
template <typename F>
std::future<typename std::result_of<F()>::type>
schedule(F f)
{
typedef typename std::result_of<F()>::type result_type;
std::packaged_task<result_type> task(f);
auto future = task.get_future();
tasks_.push_back(std::move(task)); // Queue the task, execute later.
return std::move(future);
}
然后,您可以按如下方式使用此机制:
auto future = schedule(std::bind(&f, 42, 43)); // Via std::bind.
auto future = schedule([&] { f(42, 43); }); // Lambda alternative.
if (future.has_value())
{
auto x = future.get(); // Blocks if the result of f(a,b) is not yet availble.
g(x);
}
免责声明:我的编译器不支持任务/期货,所以代码可能有一些粗糙的边缘。
关于这个问题的要点是计算 f(a,b) 和保留某种查找表以缓存结果之间的 CPU 和 RAM 的相对开销。
由于 128 位索引长度的详尽表(还)不可行,我们需要将查找空间减少到可管理的大小 - 如果在您的应用程序内部没有一些考虑,这是无法完成的:
我会简单地从一个固定大小的元组数组(a,b, f(a,b))
和线性搜索开始。根据上面询问的模式,您可能想要
(a,b,f(a,b),count)
最小计数的元组被驱逐的元组 - 这对非本地化事件有好处如果查找机制变得越来越复杂,您可能还希望根据查找机制对重复计算进行基准测试:std::map
并且朋友不是免费的,即使他们是高质量的实现。
唯一简单的方法是使用std::map
. std::unordered_map
不起作用。我们不能std::pair
在无序映射中用作键。您可以执行以下操作,
std::map<pair<int, int>, int> mp;
int func(int a, int b)
{
if (mp.find({a, b}) != mp.end()) return mp[{a, b}];
// compute f(a, b)...
mp[{a, b}] = // computed value;
return mp[{a, b}];
}