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

时间:2010-02-05 15:35:51

标签: c++ performance stl vector

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

我现在正在做:

v1.reserve(v1.size() + v2.size()); 
copy(v2.begin(), v2.end(), back_inserter(v1));

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

5 个答案:

答案 0 :(得分:59)

经过多次辩论(以及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,我们有C ++标准保证只有一次重新分配,而insert可能无效率地实现并进行多次重新分配(也可以使用特定实现进行测试) 。

答案 1 :(得分:22)

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

v1.insert(v1.end(), v2.begin(), v2.end());

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

答案 2 :(得分:18)

我只是使用以下代码和

进行了快速的性能测量
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));
    }

}

使用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%)

答案 3 :(得分:7)

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

boost::push_back(v1, v2);

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

答案 4 :(得分:3)

如果你有一个pod类型的向量,并且你真的需要性能,你可以使用memcpy,它应该比vector&lt;&gt; .insert(...)更快:

v2.resize(v1.size() + v2.size());
memcpy((void*)&v1.front(), (void*)&v2[v1.size()], sizeof(v1.front())*v1.size());

更新: 虽然我只会在性能确实如此,真的,需要时才使用它,但 代码对于pod类型是安全的。