当元素数量很少时,为什么向量优于列表?

时间:2014-07-22 22:26:53

标签: c++ vector linked-list

Bjarne Stroustrup在他的“C ++编程语言”(第4版)一书中指出:

  

请注意,vector通常是(令人惊讶的是,除非你   了解机器架构)比list更高效   短元素的小序列(即使是insert()erase())。

他没有详细说明,所以我想知道为什么它是真的,大致有多短这些序列(即元素的数量)?

2 个答案:

答案 0 :(得分:1)

  

......大致有多短这些序列

正确的答案是:你必须衡量。

这取决于您在容器中存储的内容,构建,复制和移动的类型,它的大小以及访问和插入模式的费用。

它也会因机器和编译器而异。


回答问题的第一部分:

  

...所以我想知道为什么这是真的......

在这种情况下,它的真实之处在于,矢量的连续且最小填充的存储器布局与现代CPU协作得更好。缓存子系统而不是列表。

  • vector

    • 您的循环可能类似于

      for (size_t i=0; i < v.size(); ++i) {
      

      其中v.size()通常只能被调用一次,并且iv[i+1]都适合寄存器。如果其他依赖项允许,分支预测可以很好地允许多个连续迭代运行交错,保持指令管道已满

    • 您的下一次访问(v[i])可能已在缓存中,因此非常快。它与list或同一个缓存行位于同一缓存行中,顺序读取相对容易预取

    • 但是,您的许多值都适合缓存行,即每个缓存行获得的值。在它们之间没有(不必要的)填充,因此没有不必要的缓存未命中
  • for (list::const_iterator i=l.begin(); i != l.end(); ++i) {

    • 您的循环可能类似于

      i

      其中*++i的每个连续值必须从内存加载(通过前一节点的指针)

    • 您的下一次访问({{1}})可能已在缓存中,但是:

    • 但是你的许多值都适合缓存行,更少的列表节点适合:至少有两个指针(单链表中有一个)的开销。如果您的值与指针的大小相同(我们讨论的是小值),则使用列表在缓存行中获得的数量减少三分之一,从而使缓存未命中数增加三倍。

    • 另请注意,下一次迭代的推测性执行取决于加载值而不是递增寄存器:此处的缓存未命中本身不仅较慢,而且还会拖延推测性执行

答案 1 :(得分:0)

我认为这是因为列表使用动态方法来扩展列表(可能指向下一个列表,以便您可以轻松地插入特定位置而无需重新排列内存)。矢量实际上是一个简单的数组,带有一些很好的糖涂层,使其更易于使用(如果使用正确的函数,可选择例外的边界问题)。

基本上,当你实例化一个向量时,它会分配一个类型为T的小数组。当你在最后推送一个新项时,它只是将值写入已经分配的空间,如果你还没有增长太大了。但是,当您添加到列表时,它必须分配内存并将该位置存储在各种指针中(我猜这里,但链接列表的工作原理)。如果您知道矢量有多大(或者可以相当好地猜测),那么它非常有效。但是,一旦超出它的基础数组的大小,它就必须为新放大的数据结构重新分配内存,然后复制所有内容。对于向大尺寸生长载体而言,这是非常昂贵的。在我的脑海中,向量成倍增长,因此在几次失败后故障的数量会减少,但它仍然非常昂贵。