预分配矢量是否更有效?

时间:2012-08-09 16:54:30

标签: c++ stl

在C ++ Primer第四版中,由Stanley B.Lippman,Josee Lajoie和Barbara E. Moo表示:

  

因为向量有效生长,通常最好让向量   通过向元素值动态添加元素来增长   已知的。

  

习惯于使用c或java的读者可能会期望因为vector   元素是连续存储的,最好预先分配   矢量在其预期的大小。事实恰恰相反......

  

尽管我们可以预先在矢量中分配给定数量的元素,   定义空向量和添加通常更有效   它的元素。

假设这是正确的(作者和他们一样有信誉,一个是C ++本身的共同作者)那么任何人都可以给我一个证明这个陈述的案例,并解释原因吗?

3 个答案:

答案 0 :(得分:30)

取决于。

如果您不知道最终大小是多少,那么让矢量使用其分配方案进行分配(通常每次都会加倍,或者在那里的某处)。这样就可以避免为每个元素重新分配:

std::vector<int> v;

// good:
for (/* populate v */) // unknown number of iterations
{
    v.push_back(i); // possible reallocation, but not often
}

// bad:
for (/* populate v */) // unknown number of iterations
{
    v.reserve(v.size() + 1); // definite reallocation, every time
    v.push_back(i); // (no reallocation)
}

但如果你提前知道你不会重新分配,那么预先分配:

std::vector<int> v;

// good:
v.reserve(10); 
for (/* populate v */) // only 10 iterations (for example)
{
    v.push_back(i); // no reallocations
}

// not bad, but not the best:
for (/* populate v */) // only 10 iterations (for example)
{
    v.push_back(i); // possible reallocation, but not often (but more than needed!)
}

答案 1 :(得分:6)

可以。它取决于很多关于元素是什么,复制或构造它们的工作量,以及它们的数量。

如果你预先分配一个向量,你将最终调用每个元素的默认构造函数来创建空元素,然后在空格上复制该项目。如果添加元素,它可以只复制或构建元素,这可能更有效。

答案 2 :(得分:5)

我计时这个简单的例子:

#include<iostream>
#include<vector>

int main() {

    int limit = 100 * 1000 * 1000;
    std::vector<long> my_vec;
    my_vec.reserve(limit); // comment out this line to not preallocate

    for (int i=0; i < limit; i++) {
        my_vec.push_back(i);
    }

    long my_sum = 0;
    for (int i=0; i < limit; i++) {
        my_sum += my_vec[i];
    }

    std::cout << my_sum << std::endl;
    return 0;
}

遵守:

g++ -std=c++11 -O2 my_file.cpp -o my_exec

发现差异很大:

没有预分配:

real    0m3.366s
user    0m1.656s
sys     0m1.660s

预先分配:

real    0m1.688s
user    0m0.732s
sys     0m0.936s

我的结论是:如果构建向量是程序的重要组成部分,那么预先分配效率是有道理的。然而,不可能一次又一次地构建更大的矢量,因此它很少是瓶颈。但是,除了预分配外,使用reserve()还有其他优点。

C ++编程语言中的Bjarne Stroustrup(第4个补充)有这样的说法:

  

我在阅读时曾经小心使用reserve()   向量。我很惊讶地发现,基本上我的所有用途,   调用reserve()并没有显着影响性能。默认   增长战略与我的估计一样有效,所以我停了下来   尝试使用reserve()提高效果。相反,我用它来   提高重新分配延迟的可预测性并防止   指针和迭代器失效。