81

vector的性能利器:reserve

 5 years ago
source link: https://www.tlanyan.me/reserve-of-vector/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

vectorC++ 编程时的常用容器,其帮助用户自动管理存储空间,简单易用,且能避免资源泄露的问题。需要动态分配存储空间的场景,完全可替代原生数组。

vector 被人诟病的地方在于性能。 C++ 11 引入 array 容器,有原生数组的性能,编译期能确定大小的情况可取代 vector 。但对于运行期才能确定大小的情况, array 不适用,还是得上 vector

实践中提高 vector 性能的要点是 尽量使用reserve (仅次于换编译器和STL实现)。运行期依然不能确定数组的个数,明智的选择是什么也不做, push_back/emplace_back 就足够;运行期能确定个数,则应该用 reserve ,不建议用传递大小的数组构造函数或者调用 resize

reserve vs resize

reserveresize 函数都能分配足够容纳下指定个数对象的空间。不同之处是 resize (或构造函数传递数组个数)会改变数组的 size (即当前元素的指针),并且极大的可能性会调用存储对象的 (复制)构造函数reserve 做的事情就比较纯粹:仅分配所需的空间。

一段代码说明三者的区别:

// file: test.cpp
#include <iostream>
#include <vector>


class Foo {
    public:
        Foo() {
            std::cout << "Foo constructor" << std::endl;
        }
};

int main(int argc, char* argv[]) {
    std::cout << "initialize vector with element number..." << std::endl;
    std::vector<Foo> vec1(5);
    std::cout << "-------------" << std::endl;
    std::cout << "vec1 size:" << vec1.size() << std::endl << std::endl;

    std::cout << "vector resize..." << std::endl;
    std::vector<Foo> vec2;
    vec2.resize(5);
    std::cout << "-------------" << std::endl;
    std::cout << "vec2 size:" << vec2.size() << std::endl << std::endl;


    std::cout << "vector reserve..." << std::endl;
    std::vector<Foo> vec3;
    vec3.reserve(5);
    std::cout << "-------------" << std::endl;
    std::cout << "vec3 size:" << vec3.size() << std::endl << std::endl;

    return 0;
}

gcc 编译程序: g++ -std=c++0x -o test -O2 test.cpp ,然后 ./test 执行程序,结果如下:

initialize vector with element number...
Foo constructor
Foo constructor
Foo constructor
Foo constructor
Foo constructor
-------------
vec1 size:5

vector resize...
Foo constructor
Foo constructor
Foo constructor
Foo constructor
Foo constructor
-------------
vec2 size:5

vector reserve...
-------------
vec3 size:0

输出结果很显然:无论用构造数组时指定个数,还是 resize ,都会调用存储类型的默认构造函数并改变 size 。常规情况下,默认生成的对象没什么用处,最后都会被覆盖掉。如果存储类型的构造函数比较复杂,这两种方式都以大代价做无用功。

reserve 就不会,理念简单,性能杠杠的。

注意:编译上述程序,去掉 -std=c++0x 选项,结果将会不同。原因是 vector 的构造函数或者 resize 默认用 复制 的方式填充,所以构造函数仅调用一次。

总结

细心对比,你会发现 reserveresize 的区别就像 mallocnew 的区别(不过 malloc 是函数, new 是操作符): malloc/operator new/reserve 得到一块冰冷的内存,什么都没有; new/resize 得到的是一个个鲜活的对象。如果仅需存储空间的话, malloc/operator new 会更高效。

所以,使用 vector 时尽量用 reserver 来提高性能。

参考

  1. std::vector::resize
  2. Does the vector.resize() method calls the default elements constructors when resizing?

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK