将一个std :: vector附加到另一个stnd :: vector的最有效方法是什么?

Ale*_*ter 50 c++ performance stl vector

设v1为目标向量,v2需要附加到后面.

我现在在做:

v1.reserve(v1.size() + v2.size()); 
copy(v2.begin(), v2.end(), back_inserter(v1));
Run Code Online (Sandbox Code Playgroud)

这是最有效的方式吗?或者它可以通过复制一块内存来完成?谢谢!

Kor*_*icz 63

经过大量争论(以及Matthieu M.和villintehaspam的合理评论),我会将我的建议改为

v1.insert( v1.end(), v2.begin(), v2.end() );
Run Code Online (Sandbox Code Playgroud)

我会在这里保留以前的建议:

v1.reserve( v1.size() + v2.size() ); 
v1.insert( v1.end(), v2.begin(), v2.end() );
Run Code Online (Sandbox Code Playgroud)

后一种方式有一些理由,尽管它们都不够强大:

  • 无法保证向量的重新分配大小 - 例如,如果总和大小为1025,则可能会重新分配到2048 - 取决于实现.reserve两者都没有这样的保证,但对于具体的实施,它可能是真的.如果寻找瓶颈,那么检查它可能是一件很难的事.
  • 保留陈述我们的意图明确 - 在这种情况下优化可能更有效(保留可以在一些顶级实现中准备缓存).
  • 另外,reserve我们有一个C++标准保证只有一次重新分配,同时insert可能效率低下并进行多次重新分配(也可以用特定的实现进行测试).

  • @villintehaspam - theres也****在标准中没有**保证插入不会进行多次重新分配而不是一次.然而,**是保证的保证:`保证在调用reserve()之后发生的插入期间不会发生重新分配,直到插入使向量的大小大于指定的大小为止在最近一次调用reserve().因此,储备更安全. (9认同)
  • 保留很可能是不必要的,因为这可能是由插入功能自动完成的. (6认同)
  • @Kornel:我可能不会使用`reserve`并信任`insert`.还有一个复杂的混乱.由于使用了不断增长的策略(通常是"2*x + 1"),`push_back`已经摊销了常数的复杂性,因此即使多次重新分配`insert`也会产生摊销的线性复杂性.请注意,如果您使用"RandomAccessIterator"以外的任何其他内容进行插入,最好不要计算范围的长度而只是"push_back"......我不知道他们是否为`random_access_iterator_tag`做了特殊情况. (3认同)
  • @ceretullis:这里的比较是无关紧要的,因为OP要求`v1`是目标,并且`v2`被附加到它.有时订单很重要.此外,如果我要比较,我宁愿检查容量而不是大小,看看是否足够大以包含所有数据.如果你必须扩展一个的容量,你最终会复制两者的内容,副本的顺序并不重要...... (2认同)

Unc*_*ens 22

使用专用方法可能更好更简单:vector.insert

v1.insert(v1.end(), v2.begin(), v2.end());
Run Code Online (Sandbox Code Playgroud)

正如迈克尔所提到的,除非迭代器是输入迭代器,否则向量将计算出所需的大小并一次性复制附加数据并具有线性复杂度.

  • 只要迭代器是前向,双向或随机访问,向量的最终大小将被预先确定并保留.所以没有必要预先执行`reserve()`.如果迭代器恰好是输入迭代器,则无法完成,并且可能必须多次重新分配向量,这取决于最终添加的项目数量. (5认同)
  • @Kornel Kisielewicz:这是不正确的,保留也可能分配比所需更多的内存. (2认同)

Ste*_*fan 19

我只是使用以下代码进行了快速的性能测量

v1.insert( v1.end(), v2.begin(), v2.end() );
Run Code Online (Sandbox Code Playgroud)

似乎是正确的选择(如上所述).不过,您会发现下面报告的性能.

测试代码:

#include <vector>
#include <string>

#include <boost/timer/timer.hpp>

//==============================================================================
// 
//==============================================================================

/// Returns a vector containing the sequence [ 0, ... , n-1 ].
inline std::vector<int> _range(const int n)
{
    std::vector<int> tmp(n);
    for(int i=0; i<n; i++)
        tmp[i] = i;
    return tmp;
}

void test_perf_vector_append()
{
    const vector<int> testdata1 = _range(100000000);
    const vector<int> testdata2 = _range(100000000);

    vector<int> testdata;

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  push_back()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;
        for(size_t i=0; i<testdata2.size(); i++)
        {
            testdata.push_back(testdata2[i]);
        }
    }

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  reserve() + push_back()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;
        testdata.reserve(testdata.size() + testdata2.size());
        for(size_t i=0; i<testdata2.size(); i++)
        {
            testdata.push_back(testdata2[i]);
        }
    }

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  insert()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;

        testdata.insert( testdata.end(), testdata2.begin(), testdata2.end() );
    }

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  reserve() + insert()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;

        testdata.reserve( testdata.size() + testdata.size() ); 
        testdata.insert( testdata.end(), testdata2.begin(), testdata2.end() );
    }

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  copy() + back_inserter()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;

        testdata.reserve(testdata.size() + testdata2.size()); 
        copy(testdata2.begin(), testdata2.end(), back_inserter(testdata));
    }

    printf("--------------------------------------------------------------\n");
    printf(" METHOD:  reserve() + copy() + back_inserter()\n");
    printf("--------------------------------------------------------------\n");
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;

        testdata.reserve(testdata.size() + testdata2.size()); 
        copy(testdata2.begin(), testdata2.end(), back_inserter(testdata));
    }

}
Run Code Online (Sandbox Code Playgroud)

使用Visual Studio 2008 SP1,x64,发布模式,/ O2/LTCG,输出如下:

--------------------------------------------------------------
 METHOD:  push_back()
--------------------------------------------------------------
 0.933077s wall, 0.577204s user + 0.343202s system = 0.920406s CPU (98.6%)

--------------------------------------------------------------
 METHOD:  reserve() + push_back()
--------------------------------------------------------------
 0.612753s wall, 0.452403s user + 0.171601s system = 0.624004s CPU (101.8%)

--------------------------------------------------------------
 METHOD:  insert()
--------------------------------------------------------------
 0.424065s wall, 0.280802s user + 0.140401s system = 0.421203s CPU (99.3%)

--------------------------------------------------------------
 METHOD:  reserve() + insert()
--------------------------------------------------------------
 0.637081s wall, 0.421203s user + 0.218401s system = 0.639604s CPU (100.4%)

--------------------------------------------------------------
 METHOD:  copy() + back_inserter()
--------------------------------------------------------------
 0.743658s wall, 0.639604s user + 0.109201s system = 0.748805s CPU (100.7%)

--------------------------------------------------------------
 METHOD:  reserve() + copy() + back_inserter()
--------------------------------------------------------------
 0.748560s wall, 0.624004s user + 0.124801s system = 0.748805s CPU (100.0%)
Run Code Online (Sandbox Code Playgroud)


Man*_*uel 7

如果您碰巧使用Boost,可以从Boost Vault下载RangeEx库的开发版本.这个lib.很久以前被Boost接受了,但到目前为止还没有与主要版本集成.在其中你会找到一个新的基于范围的算法,它完全符合你的要求:

boost::push_back(v1, v2);
Run Code Online (Sandbox Code Playgroud)

在内部,它的工作方式与UncleBens给出的答案类似,但代码更简洁,更易读.