【问题标题】:Push_back faster than insert?Push_back 比插入快?
【发布时间】:2016-07-20 14:00:44
【问题描述】:

我正在使用std::deque。我确信用一个push_back 替换一个循环insert 会提高性能。也推荐使用,例如here

但现在我不再那么确定了。

我已经对测试代码运行了一些基准测试。

Main.cpp:

#include"queueInsert.h"

#include<Windows.h>

std::deque<int> queue;

constexpr size_t len = 64;

int arr[len];

int main()
{
    DWORD startTime = GetTickCount();
    for (int i = 0; i < 100000; ++i)
    {
        insert(queue, arr, len);
    }
    DWORD endTime = GetTickCount();

    return endTime - startTime;
}

queueInsert.h:

#include<deque>

void insert(std::deque<int>&, int* arr, int n);

queueInsert.cpp -push 版本

#include "queueInsert.h"

void insert(std::deque<int>& queue, int* arr, int n)
{
    for (int i = 0; i < n; ++i)
    {
        queue.push_back(arr[i]);
    }
}

queueInsert.cpp -插入版本

#include "queueInsert.h"

void insert(std::deque<int>& queue, int* arr, int n)
{
    queue.insert(queue.end(), arr, arr + n);
}

我得到203 毫秒与push_back,但218insert

len 更改为6,并将迭代次数增加到一百万,保持相同的结果:219 磨削push266 磨削insert

只有在 len = 640 的情况下,push 才会输掉,即使这样输得也很少:1531 代表 push1437 代表 insert

我正在 Windows 10 下的 VisualStudio 2015 版本中编译。

我确定编译器不会像内联迭代次数或融合循环那样进行优化,因为每次我更改实现时,只会重新编译 queueInsert.cpp

我做的分析错了吗?或者如果要插入的元素数量不大,我是否应该保留push_back

【问题讨论】:

  • 我确定编译器没有进行优化 -- 让我们看看汇编列表。
  • 我读过原创文章,没关系
  • 我的意思是向量作为元素序列,而不是std::vector。我已更正以使意思更清楚。
  • "如果要插入的元素数量不太可能很大,我是否应该实际保留 push_back?" 考虑到在这种情况下性能差异是多么微不足道,做任何让你的代码看起来最合理的事情。
  • 这篇文章是2011年的。

标签: c++ performance stl


【解决方案1】:

deque::insert实际上有3种可能的操作方式:一般插入、前插入、后插入。正因为如此,每次调用insert 时,它都必须做一个测试,看看它需要以哪种方式插入。所以它必须针对前面和后面测试你传递的迭代器。

deque::push_back 只有一种操作模式:插入到后面。

使用批量插入操作的优点是容器可以准确检测它需要分配多少内存来执行整个插入,因为它可以获得迭代器范围的长度。所以批量插入越大,insert 就越好。

嗯,至少对 vector 更好。

参见vector,如果您一次插入一个 30,000 个元素,您可能会执行大约 14-15 次重新分配。这意味着分配新内存并将旧数据复制到该内存中。而如果你一次插入 30,000 个元素,你会得到一个 single 重新分配。

deque 通常实现为固定大小的块数组。因此,如果您一次插入 30,000 个元素,您将获得约 3,000 个分配(取决于块大小)。如果您一次插入 30,000 个元素,您将获得... ~3,000 个分配。所以你并没有真正节省多少。

由于 deque 的批量插入与单次插入没有太大区别,因此会发生微优化问题之间的斗争。每个insert 调用都必须进行迭代器比较以查看如何执行该插入。因此,插入越小,insert 的效率就越低。 push_back 没有这种开销,但它是每个元素的函数调用。所以它有这个开销。

因此,当每次插入添加的元素数量较多时,insert 可能会胜出。

【讨论】:

  • 我想到了它,但得出的结论是它不会那么重要,因为对 64 元素的单一检查。不过,我同意这是目前最有可能的解释。
猜你喜欢
  • 2021-03-11
  • 2014-07-06
  • 1970-01-01
  • 2016-08-27
  • 1970-01-01
  • 2019-12-22
  • 1970-01-01
  • 2020-06-20
  • 2014-03-30
相关资源
最近更新 更多