最快插入C ++

时间:2014-01-06 23:02:33

标签: c++ arrays vector

如果数组/向量非常大,有没有办法在数组/向量中的任何位置插入数据?

如果我使用vector :: insert,向量将移动我的项目之后的所有项目,这将花费很多时间,例如,如果向量得到1b项并且这是在向量的中间执行,则向量将移动500米项目。

使用C style / C ++数组或Vector吗?

是否有任何有效的方法

2 个答案:

答案 0 :(得分:7)

理论(特别是Big O表示法)表示链接列表具有O(1)插入和删除的复杂性,动态数组具有O(n),这是由于擦除/插入需要的容器移位。

但那就是理论。计算机不仅仅是理论。在实践中它的差异很大

现代计算机的内存排列在所谓的内存层次结构中,即按照其速度排序的一组不同类型的内存设备:

+---------------+
| CPU registers |   ^
+---------------+   |
|   L1 cache    |   |
|      ...      |   |   Less apacity
|   LN cache    |   |   Faster access
+---------------+   |   More expensive hardware
|      RAM      |   |
+---------------+   
|      HDD      |
+---------------+

如图所示,层次结构是通过内存访问的速度来组织的。但请注意,更高的速度意味着更昂贵的硬件,因此这转换为许多慢速访问内存和一些快速内存。 因此提高程序性能的一种方法是在快速内存中保存频繁使用的数据,并且只在必要时才转到慢速内存(程序请求未加载到快速内存中的数据,例子)。

这就是硬件的作用,假设有两种行为:

  • 如果请求了一条数据,将来可能会请求靠近它的数据。这称为Locality of reference。例如,考虑我们如何使用数组。
  • 如果请求了一条数据,将来可能会再次请求。那称为时间局部性。同样,迭代迭代和数组反复是一个例子。

当然,内存是有限的,因此将在层次结构的某个级别加载的新数据请求会丢弃之前的数据。

所以,为什么这对于不同容器的性能很重要?

记住链接列表(std::list是一个链接列表)如何炒作:
链表是通过指针在它们之间连接的分离节点链:

+---+     +---+             +---+
| 1 | --> | 2 | --> ... --> | N |
+---+     +---+             +---+

另一方面,动态数组(std::vector是动态数组)是连续的内存块

+---+---+-----+---+
| 1 | 2 | ... | N |
+---+---+-----+---+

正如我上面所说,理论上说链表插入/删除具有O(1)复杂性,因为“只是改变指针”。但是考虑一下你如何访问内存来做到这一点。 您是否注意到流程不符合空间位置规则?。因此,这有很多未命中(缓存未命中),即请求快速内存中的新内存,并且性能下降。
事实上,即使理论上说跨越链表具有O(n)复杂性,实际上与复杂性一起是由于缓存未命中导致的连续性能命中。

现在考虑动态数组是如何工作的:插入/移除具有O(n)复杂度是正确的,因为你必须在数组的一侧移动一个位置为新元素留一个间隙,或者如果你正在擦除。
请记住,数组是一个连续的内存块,所以如果你使用它,可能该数组完全(或几乎部分)加载到快速内存中( sapce-locality ),所以轮班过程真的很快

正如您所看到的,动态数组远远快于现代架构中的链接列表

一般来说,std::vector有许多与std::list相对应的优势:

  • 它的缓存 - frindly。正如我们所讨论的那样,std::list不是,而且“缓存友好”对于当今的性能非常重要。这样可以快速插入/移除,快速随机访问和快速复制。
  • 链接列表每次插入/删除都会执行一次动态内存操作。调用malloc() / free()(即,调用操作系统来检索/离开堆内存)需要花费大量时间。另一方面,动态数组仅以O(logn)平均值进行de / allocations。

但不是全部:在少数情况下,std::list必须是首选,复制/移动元素的成本非常高的情况。 std::vector性能的要点是基于在缓存中完成的廉价转换过程,但是如果向量的元素具有某些昂贵的复制/移动语义,则根本没有任何好处并且列表执行比矢量更好。

阅读this article以获取有关该主题的更多信息。

答案 1 :(得分:2)

C ++向量实际上是具有智能调整大小的数组,如果需要在容器的前面插入,则需要考虑其他类似链接列表。但是,请记住,如果性能对您非常关键,那么这些其他容器通常在内存中不连续,因此当您遍历它们并在内存中跳转时,通常会引发内存分页问题。这总是一种权衡。