代码之家  ›  专栏  ›  技术社区  ›  Alex Jenter

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

  •  46
  • Alex Jenter  · 技术社区  · 15 年前

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

    我现在在做:

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

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

    5 回复  |  直到 8 年前
        1
  •  57
  •   mloskot    13 年前

    经过多次争论(以及马修姆和维林泰哈斯潘的合理评论),我将把我的建议改为

    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可以在一些顶级实现中准备缓存)。
    • 此外,与 储备 我们有一个C++标准保证只有一个重新分配,而 insert 可能会执行效率低下,并进行多次重新分配(也可以用特定的实现进行测试)。
        2
  •  22
  •   UncleBens    15 年前

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

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

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

        3
  •  18
  •   Stefan    12 年前

    我只是用下面的代码做了一个快速的性能测量,

    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%)
    
        4
  •  7
  •   Manuel    15 年前

    如果您碰巧使用boost,您可以下载Rangeex库的开发版本。 from the Boost Vault . 这个库。不久前就被接受为Boost,但到目前为止还没有与主发行版整合。在它中,您将发现一种新的基于范围的算法,它可以完全满足您的需要:

    boost::push_back(v1, v2);
    

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

        5
  •  3
  •   Viktor Sehr    15 年前

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

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

    更新: 虽然我只会在表演真的是 真正地 需要,代码 吊舱类型安全。