C ++:用于大型矢量/双端队列的容器替换

Posted

技术标签:

【中文标题】C ++:用于大型矢量/双端队列的容器替换【英文标题】:C++: container replacement for vector/deque for huge sizes 【发布时间】:2012-07-25 05:31:30 【问题描述】:

所以我的应用程序有包含 1 亿个甚至更多元素的容器。

我正在寻找一个容器,它在整个容器中的频繁插入和删除......包括在容器附近中间。对第 n 个元素的访问时间不需要像向量一样快,但应该比 std::list 中的完全遍历更好(无论如何每个元素都有巨大的内存开销)。

元素应按索引排序(如向量、双端队列、列表),因此 std::set 或 std::unordered_set 也不能​​正常工作。

在我坐下来自己编写这样一个容器之前:有人见过这样的野兽吗?我很确定 STL 没有这样的东西,寻找 BOOST 我没有找到可以使用的东西,但我可能错了。

有什么提示吗?

【问题讨论】:

喜欢这个话题,因为我曾经编写过 32GiB 和更大的数据集(例如用于地形渲染的四叉树)(带有内存映射)。但我必须问“你已经看过什么了”:) 请注意,对于中间的操作,vector 的性能比 deque 更好...尤其是如果您最初添加 reserve(100 million) @phresnel 我查看了容器描述中的 STL 和 Boost (v1.50) 是否其中一个容器声称拥有我需要的东西,但运气不好。 @BoPersson 是的,确实如此。但是在插入时,双端队列更快,在容器的前 40% 中删除。我曾经对一个 50m 的元素容器进行基准测试,随机插入和删除,但在每个位置一次。由于末端不断插入/删除的额外好处,差异小到足以让我接受双端队列。 @BaCh:顺便说一句,1 亿并不是一个大数字,这只是 95 兆。这些元素实际上有多大? 【参考方案1】:

如果您的应用以此类数据为中心,则可以使用完整的 STL 替代大数据:

STXXL - http://stxxl.sourceforge.net/

编辑:我实际上回答得有点快。 1亿并不是一个很大的数字。例如,如果每个元素都是一个字节,则可以将其保存在 96MiB 数组中。因此,无论 STXXL 是否有用,元素的大小都应该大得多。

【讨论】:

不错,但不是我需要的。数据仍然需要保存在内存中,因为对它的访问有时非常随机,我想这会破坏磁盘。【参考方案2】:

我认为您可以通过跳过列表获得所需的性能特征:

https://en.wikipedia.org/wiki/Skip_list#Indexable_skiplist

当然,这是您感兴趣的“可索引”部分——您实际上并不希望对项目进行排序。所以需要一些修改,我把它留作练习。

您可能会发现 1 亿个列表节点开始占用 32 位地址空间,但在 64 位中可能不是问题。

【讨论】:

是的,这很好。我已经在我的一个更复杂的课程中有类似的东西,但现在希望有一个已经存在的课程......练习很好,但如果我可以自己写一个,我可以更多地专注于我的程序部分,实际上只需要这样一堂课。但迄今为止最好的答案。【参考方案3】:

1) 如果数据是高度稀疏的,即有很多零或者可以这样表达,我强烈推荐一种利用这一点的数据结构:

sparselib++ 用于矩阵 sparsehash 用于哈希映射

2) 哈希映射应该为您描述的所有操作执行 O(1),并且我之前提到的 sparsehash 实现特别节省空间;它还包括一个sparsetable 类型,它更底层一些,可以用来代替数组。

3)如果严格的排序不是那么重要(可能是,因为你提到元素应该按索引排序),你可以swap你想擦除到向量末尾的元素然后@ 987654326@ 在 O(1) 中进行删除。插入只是push_back

【讨论】:

说到节省空间的哈希映射,还有CMPH。 啊,我忘了说稀疏。不,数据一点也不稀疏,因为在具有 n 个元素的容器中,所有 n 个元素都被使用。不幸的是,严格的排序很重要。 @BaCh,你到底用索引做什么? insertremove 和检索频率之间是否可能存在数量级差异,以便我们可以优先考虑? 我通常使用迭代器来遍历容器,但有时需要直接访问第 n 个元素。然后我真的需要容器中的第n个元素,因为容器代表一个有序的序列(嗯,按索引排序)。【参考方案4】:

尝试哈希映射。 STL 有几个,都带有无序命名前缀,例如 unorderd_map 等。它具有恒定的时间插入和查找给定一个好的散列算法。使用您的“庞大”数据集,哈希图很可能会满足您的需求。对应用程序进行细微的更改以涵盖接口的差异是微不足道的。

【讨论】:

maps 和 hash_maps 未能满足“按索引排序”的先决条件。 @BaCh key value pair , int , data ... int 是索引。简单的修复。 除了一半项目的索引在您执行插入或删除时会更改,因此需要更新一半的哈希映射。不是最优的。 也许您可以在地图上使用真的稀疏密钥算法?如果您对 1 亿个项目使用 32 位密钥,那么每个项目都有 40 多个键值范围。使用 64 位意味着每个项目有 180+ 十亿个键值范围。你事先知道你有多少物品吗?当然,您必须检查冲突并加以考虑,但这很容易(在插入之前查找,如果存在,则根据您想要执行的操作移动到下一个或上一个元素,将元素键的一半相加,然后有新的钥匙) @ierceg 不错的主意。我需要检查 100m 元素的内存开销,但绝对值得一试,因为初始元素之间的 180 b 键值已经绰绰有余。使用 STL,我只能获取 map,而不是 unordered_map,因为我还需要(按顺序)从第一个元素到最后一个元素遍历数据集。不过需要检查 Google 稀疏库。

以上是关于C ++:用于大型矢量/双端队列的容器替换的主要内容,如果未能解决你的问题,请参考以下文章

C++Primer 第九章

python使用ssdb的队列,用于替换canal+rabbitmq

175. 电路维修双端队列广搜

C++stack&queue(栈队列优先级队列)

C++stack&queue(栈队列优先级队列)

C++stack&queue(栈队列优先级队列)