如何避免在std::unordered_map上进行双重搜索,并在实现缓存时避免在不需要时调用工厂函数
How to avoid double search on std::unordered_map AND avoid calling factory function when not required when implementing a cache
我一直在实现一个基于std::unordered_map的缓存。如果值已经存储,我希望避免调用生成值的工厂函数,但我也希望避免在映射上运行两次搜索。
#include <unordered_map>
struct Value {
int x;
Value & operator=(Value const &) = delete;
};
using Cache = std::unordered_map<int, Value>;
Value make_value(int i){
// Imagine that this takes a time ok!
i = i + 1;
return Value{i};
}
// This has double search
template <typename F>
Value & insert_a(Cache & cache, int key, F factory)
{
auto i = cache.find(key);
if(i==cache.end()){
auto r = cache.try_emplace(key,factory(key));
return r.first->second;
}
return i->second;
}
// This runs the factory even if it is not required
template <typename F>
Value & insert_b(Cache & cache, int key, F factory)
{
auto r = cache.try_emplace(key,factory(key));
return r.first->second;
}
int main(){
std::unordered_map<int,Value> map;
insert_a(map,10,make_value);
insert_b(map,10,make_value);
return 0;
}
我有两个insert的简化实现,演示了如何构建缓存。
insert_a使用find-first来检测项目是否存在,并且仅当它没有调用工厂来获取值时。对容器执行两次搜索。
insert_b调用try_emplace并仅返回存储的值。这显然很糟糕,因为即使值已经存在,也会调用工厂。
我似乎想要一个中间地带,在那里我直接传递工厂函数来尝试_模板,并且只有在需要时才在内部调用它。有没有办法模拟这种情况?
这不是关于如何构建缓存的一般问题。我知道多线程问题、常量正确性和可变关键字。我特别询问如何获得两个
- 容器的单次搜索
- 仅在需要时致电工厂
请注意,我故意删除了Value类的复制赋值运算符。一个显而易见的答案是先插入一个默认值,然后覆盖它。并不是所有的类都是可复制赋值的,我想支持这些。
有一个沙盒可以玩https://godbolt.org/z/Gja3MaGWf
您可以使用惰性工厂。只在需要时打电话给实际工厂:
#include <unordered_map>
#include <iostream>
struct Value {
int x;
Value & operator=(Value const &) = delete;
};
using Cache = std::unordered_map<int, Value>;
Value make_value(int i){
// Imagine that this takes a time ok!
i = i + 1;
return Value{i};
}
template <typename F>
Value & insert_b(Cache & cache, int key)
{
auto r = cache.try_emplace(key,F{key});
return r.first->second;
}
// call the factory when needed
struct ValueMaker {
int value;
operator Value() {
std::cout << "calledn";
return make_value(value);
}
};
int main(){
std::unordered_map<int,Value> map;
insert_b<ValueMaker>(map,10);
insert_b<ValueMaker>(map,10);
return 0;
}
输出为
called
因为当元素被插入到映射中时CCD_ 1仅被调用一次。在第二个调用中,值生成器(只是一个瘦包装器(不会转换为Value
,因为key
已经在映射中了。
我试着尽可能少地更改你的代码。对于实际的代码,您可能希望去掉两个工厂(make_value
和ValueMaker
(,只使用一个。关键是向try_emplace
传递一些轻包装,该包装仅在实际值转换为Value
时触发实际值的构造。
相关文章:
- 如果没有malloc,链表实现将失败
- 如何在c++中实现处理器调度模拟器
- 如何在c++中使用引用实现类似python的行为
- 实现无开销push_back的最佳方法是什么
- 使用简单类型列表实现的指数编译时间.为什么
- 如何在BST的这个简单递归实现中消除警告
- 实现一个在集合上迭代的模板函数
- 如何实现缓存友好的动态二进制树
- C++实现文件缓存(如 Web 浏览器)的库
- C++11中的无锁缓存实现
- LRU 缓存C++实现问题
- C++成员函数结果缓存实现
- LRU缓存C++实现
- 在c++中实现LRU缓存-编译错误
- 如何实现线程安全的LRU缓存回收
- 如何使用std::shared_ptr实现缓存管理器
- 如何在c++中实现与抽象类指针向量的缓存一致性
- 如何在树访问器中实现每个节点的缓存
- 实现一个倾斜关联数据缓存c++
- 实现缓存数据的c++ CGI