【问题标题】:std::vector has lower performance when size is set?设置大小时,std::vector 的性能会降低吗?
【发布时间】:2018-12-28 17:44:37
【问题描述】:

今天我想检查在 C++ 中迭代 std::vector 的三种不同方法的性能。结果并不那么令人惊讶,但其他一些事情引起了我的注意。我注意到,如果我创建一个vector 并在构造函数中预定义它的大小,有些事情会慢近两倍。请看一下结果:

default constructor...
pushing data...
elapsed: 3.85164s
for ( int i = 0; i < N; i++ ) (*vec)[i]++; - 0.127319s
for ( int i = 0; i < vec->size( ); i++ ) (*vec)[i]++; - 0.128894s
for ( int &n : *vec ) n++; - 0.127325s

vector(N) constructor...
vec.size() is now 500000000
pushing data...
elapsed: 3.93543s
for ( int i = 0; i < N; i++ ) (*vec)[i]++; - 0.126036s
for ( int i = 0; i < vec->size( ); i++ ) (*vec)[i]++; - 0.252865s
for ( int &n : *vec ) n++; - 0.254146s

N 是常数,等于 500000000。该向量仅包含 ints 个随机值 (rand())。每个循环递增所有向量元素。我正在用gettimeofday() POSIX 电话测量时间。

这是完整的源代码:

#include <iostream>
#include <vector>
#include <sys/time.h>

const int N = 500000000;

//For time measurements
static struct timeval t0, t1, tdiff;
static inline void measure_begin( )
{
    gettimeofday( &t0, NULL );
}
static inline double measure_end( )
{
    gettimeofday( &t1, NULL );
    timersub( &t1, &t0, &tdiff );
    return tdiff.tv_sec + tdiff.tv_usec / 1e6;
}


void tests( std::vector<int> *vec )
{
    ///Normal for
    std::cout << "for ( int i = 0; i < N; i++ ) (*vec)[i]++; - ";
    measure_begin( );
    for ( int i = 0; i < N; i++ ) (*vec)[i]++;
    std::cout << measure_end( ) << "s" << std::endl;

    //Normal for, but with size
    std::cout << "for ( int i = 0; i < vec->size( ); i++ ) (*vec)[i]++; - ";
    measure_begin( );
    for ( int i = 0; i < vec->size( ); i++ ) (*vec)[i]++;
    std::cout << measure_end( ) << "s" << std::endl;

    //Ranged for
    std::cout << "for ( int &n : *vec ) n++; - ";
    measure_begin( );
    for ( int &n : *vec ) n++;
    std::cout << measure_end( ) << "s" << std::endl;
}


int main( )
{
    //Create a new vector using the new constructor
    std::cout << "default constructor..." << std::endl;
    std::vector <int> *vec = new std::vector <int>;

    //Data push
    std::cout << "pushing data..." << std::endl;
    measure_begin( );
    for ( int i = 0; i < N; i++ ) vec->push_back( rand( ) ); 
    std::cout << "elapsed: " << measure_end( ) << "s" << std::endl;

    tests( vec );

    //Restart - this time set size in constructor
    delete vec;
    std::cout << "vector(N) constructor..." << std::endl;
    vec = new std::vector <int>;
    vec->reserve( N );
    std::cout << "vec.size() is now " << vec->size( ) << std::endl;

    //Data push
    std::cout << "pushing data..." << std::endl;
    measure_begin( );
    for ( int i = 0; i < N; i++ ) vec->push_back( rand( ) ); 
    std::cout << "elapsed: " << measure_end( ) << "s" << std::endl;

    tests( vec );

    delete vec;

}

我正在使用 GCC 7.30 编译代码,并在 Ubuntu 18.04 上启用了 -O3 标志。

如您所见,将数据添加到向量会稍微慢一些,而使用 std::vector(N) 构造函数对其进行修改可能会慢两倍。查看结果,在我看来,在第二种情况下,所有范围方法(vector::beginvector::end)都需要更多时间。

我无法解释这种行为的可能原因。有谁知道发生了什么?

编辑:事实证明毕竟没有性能问题。正如 Holt 在 cmets 部分中首次指出的那样,在第二种情况下,后两个 for 循环迭代两倍于默认大小的向量。当我使用vector::reserve 和默认构造函数时,一切似乎都很好。

【问题讨论】:

  • 多次运行的结果是否稳定?当您以相反的顺序运行这两个案例时,结果是否稳定?当您以不同的顺序运行 3 个单独的案例时,结果是否稳定?第一个向量在创建第二个向量之前被销毁了吗?
  • 你应该在这里发布代码。
  • @Jacajack 您的代码中有一个大问题。第一种情况在N 上循环,但在2 * N 上的秒数。如果您先执行vector&lt;int&gt;{N},然后执行N push_back,您将得到一个填充有N 零的fector,然后是N 随机值,因此您的测试花费两倍的时间是非常合乎逻辑的。
  • 如果你用一个size来构造,vector已经满了,如果你往后推,它会变得更大。如果你只想保留内存,使用reserve()方法。
  • 这与here发布的问题相同

标签: c++ performance vector


【解决方案1】:

第一个向量添加了 N 个元素。第二个向量被创建为 N 个项目,然后将 N 个项目添加到其中,使其大小增加一倍。

【讨论】:

    【解决方案2】:

    如上所述,第二个向量在创建时为 N nits 分配空间,当它被填满时无需调整自身大小,因为它不会。

    另外作为一个建议,如果你创建指定它的大小的向量,你应该使用下标运算符(operator[])替换它的元素,而不是 push_back(),因为这样你将有一个包含 N 个元素的向量,具有默认的 int 值和然后您尝试在末尾再添加一个,从而调整大小。

    请注意,调整大小时必须 cpy 之前的所有元素,因此已经有 N 个元素的向量性能更差。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多