插入或push_back到std :: vector的末尾?

jig*_*ius 15 c++ performance stl insert c++11

以下两种在a末尾插入新元素的方法在性能上是否有任何区别std::vector

方法1

std::vector<int> vec = { 1 };
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
Run Code Online (Sandbox Code Playgroud)

方法2

std::vector<int> vec = { 1 };
int arr[] = { 2,3,4,5 };
vec.insert(std::end(vec), std::begin(arr), std::end(arr));
Run Code Online (Sandbox Code Playgroud)

就我个人而言,我喜欢方法2,因为它很好且简洁,可以一次性插入数组中的所有新元素。但

  • 在性能上有什么区别吗?
  • 毕竟,他们做同样的事情。不是吗

更新资料

首先,我不使用所有元素初始化向量的原因是,在我的程序中,我根据条件添加了其余元素。

JeJ*_*eJo 13

毕竟,他们做同样的事情。不是吗

不,他们不同。std::vector::push_back与相比,使用的第一种方法将进行一些重新分配std::vector::insert

insert根据当前将在内部分配内存,std::vector::capacity复制范围之前。有关更多信息,请参见以下讨论:

std :: vector :: insert是否按定义保留?


但是性能上有什么区别吗?

由于上述原因,第二种方法将表现出轻微的性能改进。例如,使用http://quick-bench.com参见下面的快速benck标记:

查看在线基准

在此处输入图片说明

或编写一个测试程序来衡量性能(如@Some程序员在评论中提到的)。以下是一个示例测试程序:

#include <iostream>
#include <chrono>
#include <algorithm>
#include <vector>
using namespace std::chrono;

class Timer final
{
private:
    time_point<high_resolution_clock> _startTime;

public:
    Timer() noexcept
        : _startTime{ high_resolution_clock::now() }
    {}
    ~Timer() noexcept {  Stop(); }
    void Stop() noexcept
    {
        const auto endTime = high_resolution_clock::now();
        const auto start = time_point_cast<microseconds>(_startTime).time_since_epoch();
        const auto end = time_point_cast<microseconds>(endTime).time_since_epoch();
        const auto durationTaken = end - start;
        const auto duration_ms = durationTaken * 0.001;
        std::cout << durationTaken.count() << "us (" << duration_ms.count() << "ms)\n";
    }
};
// Method 1: push_back
void push_back()
{
    std::cout << "push_backing:    ";
    Timer time{};
    for (auto i{ 0ULL }; i < 1000'000; ++i)
    {
        std::vector<int> vec = { 1 };
        vec.push_back(2);
        vec.push_back(3);
        vec.push_back(4);
        vec.push_back(5);
    }
}
// Method 2: insert_range
void insert_range()
{
    std::cout << "range-inserting: ";
    Timer time{};
    for (auto i{ 0ULL }; i < 1000'000; ++i)
    {
        std::vector<int> vec = { 1 };
        int arr[] = { 2,3,4,5 };
        vec.insert(std::end(vec), std::cbegin(arr), std::cend(arr));
    }
}

int main()
{
    push_back();
    insert_range();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

使用我的系统发布版本(MSVS2019:/ Ox / std:c ++ 17AMD Ryzen 7 2700x (8核,3.70 Ghz)x64 Windows 10

// Build - 1
push_backing:    285199us (285.199ms)
range-inserting: 103388us (103.388ms)

// Build - 2
push_backing:    280378us (280.378ms)
range-inserting: 104032us (104.032ms)

// Build - 3
push_backing:    281818us (281.818ms)
range-inserting: 102803us (102.803ms)
Run Code Online (Sandbox Code Playgroud)

在给定的情况下,显示std::vector::inserting 2.7比大约快几倍std::vector::push_back

查看其他编译器(clang 8.0和gcc 9.2)根据其实现要说的内容:https : //godbolt.org/z/DQrq51

  • @jacobi Yeap。还要注意,[`std :: vector ::: reserve`](https://en.cppreference.com/w/cpp/container/vector/reserve)将使性能达到相同水平。** [请参阅此处](http://quick-bench.com/nEAR80hw3Os5kwR0oQGlOpMEL9A)**因此,如果您事先知道大小,请保留内存,并避免不必要的重新分配。 (3认同)

眠りネ*_*ネロク 12

如果向量需要重新分配,则这两种方法之间可能会有差异。

第二种方法,insert()使用迭代器范围一次调用成员函数:

vec.insert(std::end(vec), std::begin(arr), std::end(arr));
Run Code Online (Sandbox Code Playgroud)

由于insert()获得了随机访问迭代器,因此一键就能提供分配元素插入所需的所有内存的优化,即,花费一定的时间来知道范围的大小,因此整个内存分配可以复制元素之前完成操作,并且在调用过程中不会进行任何重新分配。

您的第一种方法,即对push_back()成员函数的单独调用,可能会触发多个重新分配,具体取决于要插入的元素数量和最初为向量保留的内存。

注意,上面说明的优化可能不适用于正向双向迭代器,因为要知道要插入的元素数,将花费范围大小的线性时间。但是,为这些情况分配多个内存所需的时间可能使计算范围长度所需的时间相形见,,因此它们很可能仍会实现这种优化。对于输入迭代器,由于它们是单遍迭代,因此甚至无法进行优化。


Gau*_*gal 6

主要影响因素将是重新分配。vector必须为新元素腾出空间。

考虑这 3 个 sinppets。

 //pushback
 std::vector<int> vec = {1};
 vec.push_back(2);
 vec.push_back(3);
 vec.push_back(4);
 vec.push_back(5);

 //insert
 std::vector<int> vec = {1};
 int arr[] = {2,3,4,5};
 vec.insert(std::end(vec), std::begin(arr), std::end(arr));


 //cosntruct
 std::vector<int> vec = {1,2,3,4,5};
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

为了确认重新分配,在添加vec.reserve(5)推回和插入版本后,我们得到以下结果。

在此处输入图片说明