std :: deque比std :: vector更快插入到最后?

时间:2016-02-18 14:09:26

标签: c++ stl

我开始比较:

  • 插入列表前面
  • 插入向量的背面
  • 插入deque前面

然而我注意到,即使在push_back(),deque似乎也更快。我必须做某些事情错误,我无法相信更通用的容器会胜过特定容器。

我使用谷歌基准测试的代码:

#include "benchmark/benchmark.h"
#include <deque>
#include <vector>

#define NUM_INS 1000

static void BM_InsertVector(benchmark::State& state) {
    std::vector<int> v;
    v.reserve(NUM_INS);
    while (state.KeepRunning()) {
        state.PauseTiming();
        v.clear();
        state.ResumeTiming();
        for (size_t i = 0; i < NUM_INS; i++)
            v.push_back(i);
    }
}
BENCHMARK(BM_InsertVector);

static void BM_InsertDeque(benchmark::State& state) {
    std::deque<int> v;
    while (state.KeepRunning()) {
        state.PauseTiming();
        v.clear();
        state.ResumeTiming();
        for (size_t i = 0; i < NUM_INS; i++)
            v.push_back(i);
    }
}
BENCHMARK(BM_InsertDeque);

BENCHMARK_MAIN();

结果:

Run on (1 X 2592 MHz CPU )
2016-02-18 14:03:47
Benchmark         Time(ns)    CPU(ns) Iterations
------------------------------------------------
BM_InsertVector       2820       2470     312500                                 
BM_InsertDeque        1872       1563     406977

在使用元素数量时,我注意到一些差异,但deque总是优于矢量。

编辑: 编译器:gcc version 5.2.1 使用以下内容进行编译:{{1​​}}

我认为g++ -O3 -std=c++11 push_front.cpp -lbenchmark -lpthread实际上是工具性的;当我关闭它时,我会得到一个稍微更糟的 deque性能。

2 个答案:

答案 0 :(得分:1)

我认为向量较慢,因为你正在调用clear(),这取决于你的STL实现,可能会释放底层数组存储。

如果是这种情况,那么您的reserve()电话无效;并且您的矢量不断调整大小,这需要将每个元素移动到新的更大的存储空间。

答案 1 :(得分:1)

持续将元素附加到动态容器中基本上涉及3个成本来源:

  1. 内存管理。
  2. 容器的内部簿记。
  3. 需要对元素本身执行的任何操作。值得注意的是,任何使插入时引用无效的容器都可能会移动/复制元素。
  4. 让我们从1开始。vector一直要求内存加倍,deque分配固定大小的块(deque通常实现为数组数组,下层数组具有固定的大小)。要求更多的内存可能需要更长的时间,但通常要求更少,但通常情况下,除非你的堆非常分散,一次性要求一个大块是获得一些内存的最快方法。分配一个meg一次可能更快,然后要求千字节1000次。所以很明显vector最终会在这里有优势(直到容器如此之大,它受到碎片的影响)。然而,这最终不是:你只要求1000个元素。我写了以下代码http://coliru.stacked-crooked.com/a/418b18ff8a81c1c0。它不是很有趣,但它基本上使用了一个简单的分配器来增加全局,以查看执行了多少分配。

    在您的基准测试过程中,vector要求内存11次,而deque只要求10次。deque一直要求相同的金额,vector要求加倍金额。同样,vector必须调用free 10次。并且deque 0.这似乎是deque的小胜利。

    对于内部簿记,vector的实施比deque更简单。毕竟,vector只是一个动态数组,deque是一个数组数组,严格来说更复杂。所以这显然是vector的胜利。

    最后,关于操作本身的元素。在deque中,没有任何东西被移动过。使用vector,每个新的堆分配也涉及移动所有元素。它可能已经优化为使用memcpy来处理普通类型,但是甚至可以看到,这是对memcpy的10个调用来复制1,2,4,8 ... 512个整数。这显然是deque的胜利。

    我可以推测,启动O3允许在deque中非常积极地内联许多更复杂的代码路径,减少2的权重。但显然,除非你做得更详细(非常小心!)基准,你永远不会知道。

    大多数情况下,这篇文章表明它比简单的专业容器更复杂,而不是更普遍的容器。我会做一个预测(把我的脖子剪掉,就像它一样):如果你增加元素的数量甚至是2或4的因子,你就不会再看到deque获胜了。那是因为deque将使堆分配的数量增加2倍或4倍,但向量只会增加1-2倍。

    我在此可能会注意到deque实际上是一种奇怪的数据结构;它理论上是一个数组数组,但在许多实现中,数组要么是一定的大小,要么只是一个元素,无论哪个更大。此外,它的一些大O保证是无稽之谈。 push_back只是固定的固定时间,因为在C ++中,只对元素本身的操作计入大O.否则应该很清楚,因为它是一个数组数组,顶级数组的大小成比例到已存储的元素数量。最终,顶级阵列的空间不足,你必须重新分配它,移动O(N)指针。所以这不是真的O(1)push_back