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

Which stl container should I use when doing few inserts?

本文关键字:容器 stl 我应该 插入      更新时间:2023-10-16

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

我不需要记住位置,但我也不需要随机访问。我想要快速插入。deque和vector在插入时要付出沉重的代价吗?我应该使用列表吗?

我的另一个选择是有一个第二个deque列表,当我浏览每个元素时,将其插入到另一个deque名单,除非我需要进行我所说的插入。这确实需要快速,因为它是一款性能密集型应用程序。但我使用了很多指针(每个元素都是一个指针),这让我很沮丧,但没有办法解决这个问题,所以我应该假设一级缓存总是会丢失?

在这种情况下,我会从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_back的测距插入速度快于vector的测距插入。

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

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

如果您不关心内存开销,也不需要随机访问,但您确实关心持续时间插入,那么list可能非常适合您。

std::vector具有足够的容量时,它将在结束时具有恒定的时间插入。当容量超过时,它需要一个线性时间拷贝。deque更好,因为它链接了离散的分配,避免了完整的拷贝,并允许您在前端进行恒定的时间插入。随机插入(每20个元素)将始终是线性时间。

至于缓存位置,vector是尽可能好的(连续内存),但您说过您关心插入而不是查找;根据我的经验,在这种情况下,当你扫描到转储时,你不在乎缓存有多热,所以list的糟糕行为无关紧要。

当您经常想在集合中间插入元素或经常删除元素时,列表非常有用。然而,清单读起来很慢。

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

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

如果您不需要在集合的中间插入元素(您不关心顺序),我建议您使用vector。如果您可以从一开始就近似向量中引入的元素数量,那么还应该使用std::vector::reserve来从一开始分配必要的内存。传递给reserve的值不需要精确,只需要近似即可;如果它比需要的要小,矢量将在必要时自动调整大小。

您可以走两条路:列表始终是随机插入位置的选项,但由于您单独分配每个元素,这也会导致一些性能问题。在deque中插入到位的另一种选择也不好,因为每次插入都要花费线性时间。也许你在新的deque中插入的想法在这里是最好的——你支付了两倍的内存,但另一方面,你总是在第二个deque的末尾或之前的一个元素进行插入——这一切都提供了恒定的摊销时间,而且你仍然可以很好地缓存容器。

std::vector/deque ::insert等完成的副本数量与插入位置和容器末端之间的元素数量(需要移动以腾出空间的元素数量)成比例。std::vector最坏的情况是O(N)——当您在容器的前面插入时。如果要插入M元素,那么最坏的情况就是O(M*N),这并不好。

如果超过集装箱容量,还可能涉及重新分配。您可以通过确保前面有足够的::reserve'd空间来防止重新分配。

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

使用std::list可以实现原位O(1)插入,但代价是额外的内存开销(存储列表指针等)和减少的内存位置(列表节点不是连续分配的)。您可以通过使用池内存分配器来提高内存位置(可能是提升池?)。

总的来说,你必须进行基准测试,才能真正找出哪种方法是"最快"的。

希望这能有所帮助。

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

当然,最终确定哪一个是最佳解决方案的唯一方法是测量性能。