C++ 将一个std::vector追加到另一个std::vector末尾的最有效方法是什么?

C++ 将一个std::vector追加到另一个std::vector末尾的最有效方法是什么?,c++,performance,stl,vector,C++,Performance,Stl,Vector,假设v1是目标向量,v2需要附加到它的后面 我现在正在做: v1.reserve(v1.size() + v2.size()); copy(v2.begin(), v2.end(), back_inserter(v1)); 这是最有效的方法吗?或者仅仅通过复制一块内存就可以做到这一点? 谢谢 经过多次争论(以及Matthieu M.和villintehaspam的合理评论),我将把我的建议改为 v1.insert( v1.end(), v2.begin(), v2.end() ); 我将保

假设v1是目标向量,v2需要附加到它的后面

我现在正在做:

v1.reserve(v1.size() + v2.size()); 
copy(v2.begin(), v2.end(), back_inserter(v1));
这是最有效的方法吗?或者仅仅通过复制一块内存就可以做到这一点? 谢谢

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

v1.insert( v1.end(), v2.begin(), v2.end() );
我将保留前一个建议:

v1.reserve( v1.size() + v2.size() ); 
v1.insert( v1.end(), v2.begin(), v2.end() );
采用后一种方法有一些原因,但没有一个原因足够有力:

  • 无法保证向量将被重新分配到什么大小——例如,如果总和大小为1025,它可能会被重新分配到2048——这取决于实现。对于
    reserve
    也没有这样的保证,但是对于特定的实现,它可能是正确的。如果要寻找瓶颈,最好检查一下
  • reserve清楚地表明了我们的意图——在这种情况下,优化可能更有效(reserve可以在一些顶级实现中准备缓存)
  • 还有,<>代码> Reals我们有一个C++标准保证只会有一个重新分配,而 INSERT /COM>可以不有效地执行,并执行一些重新分配(也可以用特定的实现来测试)。李>
使用专用方法可能更好、更简单:


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

如果您有一个pod类型的向量,并且确实需要性能,您可以使用memcpy,它应该比vector更快。insert(…):

更新:
虽然我只会在性能确实非常需要的情况下才使用它,但代码对于pod类型是安全的。

如果您碰巧使用了Boost,您可以下载RangeEx库的开发版本。这个图书馆。一段时间前被Boost接受,但到目前为止还没有与主发行版整合。在其中,您将发现一种新的基于范围的算法,它完全满足您的要求:

boost::push_back(v1, v2);

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

我只是用以下代码和

v1.insert( v1.end(), v2.begin(), v2.end() );
似乎是正确的选择(如上所述)。 不过,您可以在下面找到报告的性能

测试代码:

#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));
    }

}

可能重复此问题的答案实际上是正确的,但不是该问题(!)保留很可能是不必要的,因为这可能是由insert函数自动完成的。@villintehaspam-标准中也不能保证insert不会进行多次重新分配,而不是一次。但是,保留有一个保证:
保证在调用reserve()后发生的插入期间不会发生重新分配,直到插入会使向量的大小大于最近调用reserve()时指定的大小为止。
因此,保留更安全。@Kornel Kisielewicz:实际上,我相信在23.2.4.4项中或多或少有这样的保证(对于普通迭代器),其中复杂性被指定为范围内元素数的线性[第一个,最后一个)加上到向量末端的距离。除非我弄错了,否则多次再分配不符合要求?@Ceretulis:这里比较是不相关的,因为OP要求将
v1
作为目标,并将
v2
附加到目标上。有时顺序很重要。此外,如果要比较,我宁愿检查容量比大小大一点,看看一个是否足够容纳所有的数据。如果你必须扩展一个的容量,你最终会复制两个的内容,而且拷贝的顺序并不重要…@Kornel:我可能不会使用
保留
和信任
插入
。另外还有一个复杂性的混淆。
push_back
由于使用了不断增长的策略(通常
2*x+1
)而分摊了恒定的复杂性因此,即使使用多次重新分配
insert
也会有一个摊销的线性复杂度。请注意,如果您使用的不是
RandomAccessIterator
的任何东西进行插入,您最好不要计算范围的长度,而只是
向后推
…我不知道他们是否对
random\a进行了特殊处理access\u iterator\u tag
。尽管我不是100%确定,因为resize会执行许多不必要的构造函数调用。@直接:这就是我使用resize而不是reserve的原因。是的,这非常快,但它使用了实现细节。可能更慢。它首先将内存设置为零,然后用正确的值覆盖它。现代STL实现自动检测POD,复制它们时将生成对齐的内存副本。因此,它在
memcpy()中保存了调零和对齐检查
如果
memcpy
确实是安全的,那么实现可能会直接调用它。只要迭代器是正向、双向或随机访问的,向量的最终大小将预先确定并保留。因此不需要执行
reserve()
在前面。如果迭代器恰好是输入迭代器,则无法执行此操作,并且向量可能必须根据最终添加的项目数重新分配几次。@Michael,有关保留的原因,请参阅我的答案。@Kornel Kisielewicz:这是错误的,保留也可能会分配超出需要的内存。@villinteha垃圾邮件-可能,这就是为什么我写了“强烈提示”我想这一切都归结于实现的质量。我更喜欢信任实现,因为我看不出技术原因为什么保留会产生任何影响。如果您的实现决定使用纯插入过度分配,那么使用保留也同样可能过度分配。
v1.insert( v1.end(), v2.begin(), v2.end() );
#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));
    }

}
--------------------------------------------------------------
 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%)