关于c ++:将一个std :: vector追加到另一个stnd :: vector的最有效方法是什么?

What is the most efficient way to append one std::vector to the end of another?

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

我现在正在做:

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

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


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

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

我将保留以前的建议:

1
2
v1.reserve( v1.size() + v2.size() );
v1.insert( v1.end(), v2.begin(), v2.end() );

后一种方法有一些原因,尽管它们都不够强大:

  • 无法保证将重新分配向量的大小——例如,如果总和大小为1025,则可能会根据实现情况重新分配到2048。对于reserve也没有这样的保证,但对于具体的实现,这可能是真的。如果寻找瓶颈的话,检查一下可能会很麻烦。
  • reserve表明我们的意图是明确的——在这种情况下优化可能更有效(reserve可以在一些顶级实现中准备缓存)。
  • 此外,对于EDCOX1,0,我们有一个C++标准保证只会有一个重新分配,而EDCOX1×2可能会被低效地执行,并做一些重新分配(也可以用一个特定的实现来测试)。


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

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

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


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

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

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

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#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("--------------------------------------------------------------
"
);
    printf(" METHOD:  push_back()
"
);
    printf("--------------------------------------------------------------
"
);
    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("--------------------------------------------------------------
"
);
    printf(" METHOD:  reserve() + push_back()
"
);
    printf("--------------------------------------------------------------
"
);
    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("--------------------------------------------------------------
"
);
    printf(" METHOD:  insert()
"
);
    printf("--------------------------------------------------------------
"
);
    testdata.clear();
    { vector<int>().swap(testdata); }
    testdata = testdata1;
    {
        boost::timer::auto_cpu_timer t;

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

    printf("--------------------------------------------------------------
"
);
    printf(" METHOD:  reserve() + insert()
"
);
    printf("--------------------------------------------------------------
"
);
    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("--------------------------------------------------------------
"
);
    printf(" METHOD:  copy() + back_inserter()
"
);
    printf("--------------------------------------------------------------
"
);
    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("--------------------------------------------------------------
"
);
    printf(" METHOD:  reserve() + copy() + back_inserter()
"
);
    printf("--------------------------------------------------------------
"
);
    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,输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
--------------------------------------------------------------
 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%)


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

1
boost::push_back(v1, v2);

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


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

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

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