"Flattening" std::set<std::string> 用于存储和比较?
"Flattening" std::set<std::string> for storage and comparison?
这可能是一个愚蠢的问题,基于std :: set&lt;>已经具有完美的比较操作员,但是我认为我可能对我的特定用例有优化为了确保我不会以某种方式伤害自己。
本质上,我有一个昂贵的操作,该操作将作为输入的std :: set&amp;。我正在缓存该操作的结果,因此,如果已经传递了相同的输入,我只能返回结果。这确实需要存储集合的副本(我在A
中进行的副本std::map<std::set<std::string>, Result*>
,然后每次调用操作时进行搜索。由于很有可能会连续将同一操作称为数千次,因此我要说的是,cached std :: Set> 99%的时间> 99%。我最近尝试了我认为可能是一个很小的改进,因为某些字符在传递字符串中是无效的事实:我将std ::设置为单个字符串,将组件字符串划定为'::' 特点。我的std ::地图变成
std::map<std::string, Result*>
每次调用操作时,集合都会扁平,并且在缓存中搜索了单个字符串。
我实际上对性能提高感到惊讶。我的测试运行使用了std ::集,其中包含5个字符串,每个30个字符的长度以及10,000,000个搜索。在我的工作站上,每次运行的时间都是
std::map<std::set<std::string>, Result*> : 138.8 seconds
std::map<std::string, Result> : 89.2 seconds
看来,即使在每次呼叫的设置上的开销中,第二种方法也是一个巨大的进步。我想我的问题是:为什么?我在这里做的可能不好的事情是有目的地避免了STD ::的实施者(即可能导致较大的字符串造成不良的堆碎片?)是仅仅是因为集合中的单个字符串在不同的位置都在不同的位置进行比较,并且必须分开比较?我在脚上开枪吗?在这种特定情况下,似乎太明显了,可以提高这种性能。
为什么?
数据位置。
std::set
通常以二进制搜索树的形式实现。与std::set
相比,由于使用std::string
的计算机,搜索操作可能更快。
我将考虑在围绕其地址和版本号的围绕集合编写一个小包装。它将包括修改集合的操作(插入,擦除等)的过载,当发生插入/擦除时,它会增加版本号。
为了确定平等,您只查看两件事:集合的地址和版本号。如果修改相当罕见,并且对平等的测试相当普遍,那么比较节省的时间可能要比跟踪变化所花费的时间大得多 - iow,您将获得很大的速度胜利。
如果您必须编写完整包装器(揭露set
的功能的全部的包装器),这可能是很多工作。在大多数情况下,这是不必要的;大多数典型的代码将仅需要几个函数可见 - 通常只有两个或三个。
#include <iostream>
#include <set>
#include <utility>
template <class T>
class tracked_set {
std::set<T> data;
size_t version = 0;
public:
typedef typename std::set<T>::iterator iterator;
std::pair<iterator, bool> insert(T &&d) {
auto ret = data.insert(std::forward<T>(d));
version += ret.second;
return ret;
}
iterator erase(iterator i) {
auto ret = data.erase(i);
if (ret != data.end())
++version;
}
// At least if memory serves, even non-const iterators on a `set` don't
// allow the set to be modified, so these should be safe.
auto begin() { return data.begin(); }
auto end() { return data.end(); }
auto rbegin() { return data.rbegin(); }
auto rend() { return data.rend(); }
// The `c*` iterator functions return const_iterator's, so
// they're definitely safe.
auto cbegin() const { return data.cbegin(); }
auto cend() const { return data.cend(); }
auto crbegin() const { return data.crbegin(); }
auto crend() const { return data.crend(); }
class token {
std::set<T> const *addr;
size_t version;
public:
friend bool operator==(token const &a, token const &b) {
return a.addr == b.addr && a.version == b.version;
}
token(tracked_set const &ts) {
addr = &ts.data;
version = ts.version;
}
};
operator token() const { return token(*this); }
};
int main() {
using T = tracked_set<int>;
T ts;
ts.insert(1);
ts.insert(2);
T::token t(ts);
if (t == T::token(ts))
std::cout << "Goodn";
ts.insert(3);
if (t == T::token(ts))
std::cout << "badn";
}
- std::原子加载和存储都需要吗
- 如何从存储在std::映射中的std::集中删除元素
- 使用 pqxx 将 std::vector 存储在 postgresql 中,并从数据库中检索它
- 在std::vector上存储带有模板的类实例
- 在 std::无符号字符的向量处存储 int 的十六进制表示形式
- 将无符号字符的向量存储在数组中会给我 std::bad_alloc
- 如何调用存储在指向"std::函数"的指针中的 lambda?
- 如何访问存储在 std::variant 中的类的方法
- std::vector move 而不是交换到空 vector 并释放存储
- 将可变参数模板类存储到 std::vector 中
- 在 std::vector<无符号字符中存储任意数据的方法>
- 是否可以将具有不同签名的 lambda 存储在 std::vector 中并在函数中执行它们(使用各自的参数)?
- "p"数组如何使用 std::normal_distribution 存储以下代码中的值C++?
- "std::string"是否将其字符作为签名字符在内部存储?
- std::set 是否将对象连续存储在内存中?
- 将数据从 std::Vector 存储到 Eigen::Vector 时出错
- C++:将 std::set_union() 输出存储在 std::multiset 中
- 使用 std::list 存储顶点并使用 SFML 绘制它们
- 从其存储的回调中删除 std::函数是否安全
- 使用 std::map 存储在 std::any 中,然后通过 std::any_cast 访问