我不知道我的确切数字,但我会尽我所能。我有一个10000元素的deque,一开始就填充了。比我扫描每个元素,让每20个元素我需要插入一个新元素。插入将发生在当前位置,可能会返回一个元素。
我不需要记住这个位置,但我也不需要随机访问。我想快速插入。 deque和vector的插入价格是否很高?我应该使用清单吗?
我的另一个选择是有第二个双端队列表,当我浏览每个元素时将其插入到另一个双端队列表中,除非我需要进行我正在讨论的插入。这确实需要快速,因为它是一个性能密集型应用程序。但是我使用了很多指针(每个元素都是一个指针),这让我感到不安但是没有办法解决这个问题所以我应该假设L1缓存总会错过?
答案 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_back
比std::list
插入快7倍。 push_back
比vector
的范围插入速度快。
正如Emile在下面指出的那样,std::vector::swap()
不需要移动或重新分配元素 - 它可以只交换内部(假设分配器是相同的类型)。
答案 1 :(得分:3)
首先,所有性能问题的答案都是“基准测试”。总是。现在...
如果您不关心内存开销,并且您不需要随机访问,但做关心是否需要进行常量时间插入,list
可能适合你。
std::vector
具有足够的容量时,>}将在末尾 strong>进行常量插入。超过容量时,需要线性时间复制。 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)
如果您需要在中间快速插入,但不关心随机访问,vector
和deque
绝对不适合您:对于那些,每次插入内容时,必须移动该一个和最终之间的所有元素。在内置容器中,list
几乎肯定是你最好的选择。但是,针对您的场景的更好的数据结构可能是VList,因为它提供了更好的缓存局部性,但是C ++标准库没有提供。维基百科页面链接到C ++实现,但是从界面的快速视图看,它似乎不完全兼容STL;我不知道这对你来说是不是一个问题。
当然,最终确定哪种是最佳解决方案的唯一方法是衡量绩效。