【发布时间】: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::begin、vector::end)都需要更多时间。
我无法解释这种行为的可能原因。有谁知道发生了什么?
编辑:事实证明毕竟没有性能问题。正如 Holt 在 cmets 部分中首次指出的那样,在第二种情况下,后两个 for 循环迭代两倍于默认大小的向量。当我使用vector::reserve 和默认构造函数时,一切似乎都很好。
【问题讨论】:
-
多次运行的结果是否稳定?当您以相反的顺序运行这两个案例时,结果是否稳定?当您以不同的顺序运行 3 个单独的案例时,结果是否稳定?第一个向量在创建第二个向量之前被销毁了吗?
-
你应该在这里发布代码。
-
@Jacajack 您的代码中有一个大问题。第一种情况在
N上循环,但在2 * N上的秒数。如果您先执行vector<int>{N},然后执行Npush_back,您将得到一个填充有N零的fector,然后是N随机值,因此您的测试花费两倍的时间是非常合乎逻辑的。 -
如果你用一个size来构造,vector已经满了,如果你往后推,它会变得更大。如果你只想保留内存,使用reserve()方法。
-
这与here发布的问题相同
标签: c++ performance vector