在进行少量插入时,我应该使用哪个stl容器
Which stl container should I use when doing few inserts?
我不知道确切的数字,但我会尽力的。我有一个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_back
比std::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)
插入,但代价是额外的内存开销(存储列表指针等)和减少的内存位置(列表节点不是连续分配的)。您可以通过使用池内存分配器来提高内存位置(可能是提升池?)。
总的来说,你必须进行基准测试,才能真正找出哪种方法是"最快"的。
希望这能有所帮助。
如果您需要在中间快速插入,但不关心随机访问,vector
和deque
绝对不适合您:对于这些,每次插入东西时,必须移动该元素和结尾之间的所有元素。在内置容器中,list
几乎可以肯定是你的最佳选择。然而,对于你的场景来说,更好的数据结构可能是VList,因为它提供了更好的缓存位置,而C++标准库没有提供。维基百科页面链接到C++实现,但从界面的快速视图来看,它似乎并不完全兼容STL;我不知道这是不是你的问题。
当然,最终确定哪一个是最佳解决方案的唯一方法是测量性能。
- 在STL容器中使用模板类
- 检查函数返回类型是否与STL容器类型值相同
- STL算法函数在多个一维容器上的使用
- 为什么 STL 容器适配器堆栈中的 top 返回常量引用?
- 如果我真的真的想从 STL 容器继承,并且我继承构造函数并删除新运算符,会发生什么?
- 对象 C++ 向量的 STL 容器
- C++ STL:将空容器传递给lower_bound
- 如何在不破坏现有应用程序的情况下更改 API 中 stl 容器的数据类型?
- 在 STL 容器的 STL 容器上调用 clear
- "迭代器"和"const_iterator"不是 STL 容器的必需成员?
- 删除包含包含动态对象的 STL 容器的智能指针
- C++:在子类中扩展静态 STL 容器/映射成员?
- 是否有具有外部元素分配的序列容器(在 STL 中)?
- ostream 运算符<< 为获取 STL 容器而过载,传递 std::string 会破坏它?
- 没有 STL 容器的迭代器
- 为什么某些 STL 容器(堆栈、队列、优先级队列)不支持迭代器?
- 接受任何 STL 容器的函数
- C++ STL 容器中优先级搜索的优雅方法
- 我们如何打印出C++ STL 容器的value_type?
- 关联容器(STL)中的等式求值