【问题标题】:Data structure for storing huge number of indices, each pointing to a set用于存储大量索引的数据结构,每个索引都指向一个集合
【发布时间】:2013-02-18 15:15:37
【问题描述】:

我在 C++ (std::map) 中使用红黑树实现,但目前,我看到我的 unsigned long long int 索引变得越来越大,以进行更大的实验。我打算使用 700,000,000 个索引,每个索引都存储一个 std::set ,其中包含更多的 int 元素(大约 1-10 个)。我们有 128 GB RAM,但我发现我们开始用完了;事实上,如果可能的话,我什至想在我的实验中减少到 1,000,000,000 个索引。

我对此进行了一些思考,并且正在考虑将几张地图放在一起的森林。基本上,在映射达到某个大小阈值后(或者可能在开始抛出 bad_alloc 时),将其保存到磁盘,将其从内存中清除,然后创建另一个映射并继续执行,直到我得到所有索引。但是,在加载部分,这将非常低效,因为我们一次只能在 RAM 中保存一张地图。更糟糕的是,我们需要检查所有地图的一致性。

那么在这种情况下,我应该寻找哪些数据结构?

【问题讨论】:

  • 你能解释一下你看到的实际问题是什么吗?
  • 您能详细解释一下这些数据是如何使用的吗?如果存储数据不太灵活,可能会更好,例如排序数组,如果它们适合该问题。
  • 问题是我想要在地图中有大量索引的接近原生速度。现在,我们遇到了内存不足的问题。
  • 如果您使用的是 Windows,请改用 ESENT(可扩展存储引擎)。
  • 或者至少使用 B-tree 代替你的 RB 树,并依赖 OS 提供的交换文件机制。

标签: c++ memory-management data-structures map


【解决方案1】:

根据你的描述,我认为你有这个:

typedef std::map<long long, std::set<int>> MyMap;

map 非常大,而单个集合非常小。这里有几个开销来源:

  • map 中的各个条目,每个条目都是单独的分配;
  • sets 中的各个条目,同上;
  • 描述每个 set 的结构,独立于其内容。

使用标准库组件,不可能消除所有这些开销;关联容器的语义很好地要求每个条目的单独分配,并且使用红黑树需要为每个条目添加几个指针(理论上,只需要两个指针,但是如果没有迭代器的有效实现是困难的父指针。)

但是,您可以通过将mapsets 结合使用来减少开销而不会丢失功能,使用如下数据结构:

typedef std::set<std::pair<long long, int>> MyMap;

您仍然可以回答所有相同的问题,尽管其中一些不太方便。请记住std::pair 的默认比较器按字典顺序排序,因此具有相同first 值的所有元素将是连续的。因此,例如,您可以使用以下方法查询给定索引是否有任何ints 关联:

it = theMap.lower_bound(std::make_pair(index, INT_MIN));
if (it != theMap.end() && it->first == index) {
  // there is at least one int associated with index
}

lower_bound 的相同调用将为您提供 ints associate with the key, while a call toupper_bound(std::make_pair(key, INT_MAX))` 的开始迭代器,将为您提供相应的结束迭代器,因此您可以轻松地遍历所有与给定键关联的值。

这可能仍然不足以在 128GB 中存储 7 亿个带有相关整数集的索引,除非平均集大小非常小。下一步必须是某种形式的 b-tree,它不在标准库中。 B-树通过将多个条目组合到一个集群中来避免单个条目的开销;这应该足以满足您的需求。

【讨论】:

  • 我将您的建议与 Zaufi 对 B-Tree 的建议 (stackoverflow.com/a/14939735/144201) 结合起来,我发现我的索引为 4.92 亿,但我现在只使用了 10.9 GB RAM。我最初的实现是在这些索引数量周围 > 70 GB。令人印象深刻。
【解决方案2】:

看来是时候切换到 B-trees(可能是 B+ 或 B*)了——这种结构在数据库中用于管理索引。看一下here——这是替换内部带有 btree 的类 std 关联容器...但 btree 可用于将索引保存在内存和磁盘上...

【讨论】:

  • +1:我正在使用 B-Tree 进行实验以测试您的建议,我发现它看起来很有希望。内存使用量显着下降。
  • 您可以采取的下一步是使用非默认分配器。尝试以更适合您的数据的方式播放...查看 boost::pool 库和/或谷歌的“小对象分配器”
【解决方案3】:

对于如此大规模的数据集,您确实应该使用合适的database server,例如SQL 服务器。这些服务器旨在处理缓存的大规模数据集。 SQL 服务器将数据保存到硬盘等永久缓存中,同时通过缓存经常访问的页面等保持良好的读/写性能。

【讨论】:

  • 虽然数据库解决方案一开始看起来很有希望,但我们已经对其进行了实验,似乎在我们达到大约一百万个索引后,性能下降到爬行,我们的 C++ 实现击败了它几个幅度。最初的几十万个指数看起来又快又猛,到了一定的极限后,速度就变慢了。
  • 我怀疑精明的 DBA 无法根据您的需要优化数据库。似乎您的服务器具有大量缓存的潜力,因此“开箱即用”的数据库可能不如专门为手头的工作配置的数据库那么有效。我不是 DBA,因此在这里无法提供任何真正的帮助,但这些平台已经推动了这个行业几十年了:已经花费了数百万小时来优化和微调他们的算法以挤出更多的性能。我真的很怀疑“内部”实施能否超越这种表现
  • 也许这些是 Windows 方面的商业/行业级支持(针对问题进行了优化)?我们在 Linux 这边。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-08
  • 2020-09-15
  • 2017-07-07
  • 2019-03-22
  • 2013-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多