在进行少量插入时,我应该使用哪个stl容器?

时间:2012-02-12 07:56:16

标签: c++ performance stl containers insertion

我不知道我的确切数字,但我会尽我所能。我有一个10000元素的deque,一开始就填充了。比我扫描每个元素,让每20个元素我需要插入一个新元素。插入将发生在当前位置,可能会返回一个元素。

我不需要记住这个位置,但我也不需要随机访问。我想快速插入。 deque和vector的插入价格是否很高?我应该使用清单吗?

我的另一个选择是有第二个双端队列表,当我浏览每个元素时将其插入到另一个双端队列表中,除非我需要进行我正在讨论的插入。这确实需要快速,因为它是一个性能密集型应用程序。但是我使用了很多指针(每个元素都是一个指针),这让我感到不安但是没有办法解决这个问题所以我应该假设L1缓存总会错过?

6 个答案:

答案 0 :(得分:4)

在这种情况下,我从std::vector开始,使用第二个std::vector进行质量突变,reserve()适当,然后swap()向量。

<强>更新

这将采用这种一般形式:

std:vector<t_object*> source; // << source already holds 10000 elements

std:vector<t_object*> tmp;

// to minimize reallocations and frees to 1 and 1, if possible.
// if you do not swap or have to grow more, reserving can really work against you.
tmp.reserve(aMeaningfulReserveValue);

while (performingMassMutation) {
  // "i scan through each element and lets every 20 elements"
  for (twentyElements)
    tmp.push_back(source[readPos++]);

  // "every 20 elements i'll need to insert an new element"
  tmp.push_back(newElement);
}

// approximately 500 iterations later…

source.swap(tmp);

Borealid提出了一个很好的观点,即 measure - 执行情况因您的std库实现,数据大小,复制的复杂性等而有很大差异。

对于具有 my 配置的此大小的集合的原始指针,上面的vector质量突变和push_backstd::list插入快7倍。 push_backvector的范围插入速度快。

正如Emile在下面指出的那样,std::vector::swap()不需要移动或重新分配元素 - 它可以只交换内部(假设分配器是相同的类型)。

答案 1 :(得分:3)

首先,所有性能问题的答案都是“基准测试”。总是。现在...

如果您不关心内存开销,并且您不需要随机访问,但关心是否需要进行常量时间插入,list可能适合你。

std::vector具有足够的容量时,}将在末尾进行常量插入。超过容量时,需要线性时间复制。 deque更好,因为它链接离散分配,避免完整副本,并允许您在前面进行常量时间插入。随机插入(每20个元素)将始终是线性时间。

至于缓存局部性,vector和你能得到的一样好(连续的内存),但你说你关心的是插入而不是查找;根据我的经验,在这种情况下,你不关心缓存在扫描到转储时有多热,所以list的不良行为并不重要。

答案 2 :(得分:2)

当您经常要在集合中间插入元素或经常删除元素时,列表很有用。但是,列表阅读速度很慢。

当您只想在集合的末尾添加或删除元素时,向量的读取速度非常快且非常快,但当您在中间插入元素时,它们非常慢。这是因为它必须将所需位置之后的所有元素移动一个位置,以便为新元素腾出空间。

Deques基本上是可以用作向量的双向链表。

如果您不需要在集合中间插入元素(您不在乎订单),我建议您使用矢量。如果您可以从头开始估计将在向量中引入的元素数,则还应使用std::vector::reserve从头开始分配所需的内存。您传递给reserve的值不一定是精确的,只是近似值;如果它小于需要,矢量将在必要时自动调整大小。

答案 3 :(得分:2)

您可以采用两种方式:list始终是随机位置插入的选项,但是当您分别分配每个元素时,这也会产生一些性能影响。在双端队列中就地插入的另一个选择也不好 - 因为你将为每次插入支付线性时间。也许你在插入新双端队列的想法在这里是最好的 - 你支付两倍的内存,但另一方面你总是插入第二个双端队列的末尾,或者之前的一个元素 - 这都给出了不变的摊销时间,你仍然可以很好地缓存容器。

答案 4 :(得分:2)

std::vector/deque ::insert等完成的副本数量与插入位置和容器末尾之间的元素数量(需要移动以腾出空间的元素数量)成正比。 std::vector的最坏情况是O(N) - 当您插入容器的前面时。如果你要插入M个元素,那么最坏的情况是因为O(M*N)而不是很好。

如果超出容器容量,也可能涉及重新分配。您可以通过确保前面有足够的空间::reserve来防止重新分配。

您还有其他建议 - 复制到第二个std::vector/deque容器可能会更好,因为它总是可以组织起来以实现O(N)复杂性,但代价是暂时存储两个容器。

使用std::list将允许您实现就地O(1)插入,但代价是额外的内存开销(存储列表指针等)和减少的内存局部性(列表节点未分配)连续)。您可以通过使用池内存分配器来改善内存局部性(可能是Boost pools。)。

总的来说,你必须通过基准来真正理清哪种是“最快”的方法。

希望这有帮助。

答案 5 :(得分:1)

如果您需要在中间快速插入,但不关心随机访问,vectordeque绝对不适合您:对于那些,每次插入内容时,必须移动该一个和最终之间的所有元素。在内置容器中,list几乎肯定是你最好的选择。但是,针对您的场景的更好的数据结构可能是VList,因为它提供了更好的缓存局部性,但是C ++标准库没有提供。维基百科页面链接到C ++实现,但是从界面的快速视图看,它似乎不完全兼容STL;我不知道这对你来说是不是一个问题。

当然,最终确定哪种是最佳解决方案的唯一方法是衡量绩效。