影响vector效率的关键

本文通过实验比较了在循环外部预申请空间并赋值、循环内部插入、以及循环内部声明并预申请空间的三种二维vector操作方法的效率。实验结果显示,外部定义并预申请空间的resize+赋值方法表现最佳,而内部定义且不预申请空间的方法与预申请空间的方法耗时接近。尽管预申请空间可以避免内存重新分配,但在循环内部声明vector的开销并不显著。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近做一定规模的数据读取,二维vector,行约300k,每列大约10~100个int。一直在纠结以下问题:

  1. 方法一:在循环的外边预先声明一个vector(预申请足够大空间),然后在循环内部做resize操作,然后对应位置直接赋值
  2. 方法二:在循环的外边预先声明一个vector(预申请足够大空间),然后在循环内部做clear操作,然后再用push_back插入
  3. 方法三:每次在循环中声明一个vector局部变量(预申请足够大空间),用push_back插入

显然内存拷贝是vector最耗时的操作,所以上述都在声明时直接指定需要的空间。如果不知道需要的空间,则取可能情况中足够大的即可。

上述三种方法是否有明显的时间差异?

直观上来说,第一种、第二种相较于第三种应该是更加快一些的,因为没有局部vector的构造与销毁。但是,方法一与方法三哪个更快呢?

当然要写代码做实验。

不过,在写之前,加入一组猜想地、可能最慢的对照实验:在方法三的基础上,每次声明vector时定义一个默认vector,即不预申请空间。定义其为方法0 ;

实验设置

平台及编译器:RedHat , GCC 4.8.5
迭代轮次: 0x100000 (1M) 次

实验结果

方法时间(s)
Method 0 (In + No-Alloc + push_back )13.2011
Method 1 (Out + Pre-Alloc + Resize + assign)1.61622
Method 2 (Out + Pre-Alloc + Clear + push_back )10.9568
Method 3 (In + Pre-Alloc + assign)2.43724

结果总结

实验结果不得不说有点让人吃惊啊….

  1. 使用外部定义且预申请空间的方法2竟然耗时与内部定义+不预申请空间的方法0在一个量级… 而他们的共同点是,用了push_back
  2. 即使每次在循环内部声明局部vector且预申请空间,但最后时间差距与方法1差别不大。

WHY

难道push_back 会导致内存的重新分配? 官方文档说:

This effectively increases the container size by one, which causes an automatic reallocation of the allocated storage space if -and only if- the new vector size surpasses the current vector capacity.

对于clear官方文档中说clear A reallocation is not guaranteed to happen

看来还需要把capacity打印一下… 有空做一下吧…

结论

  1. 在循环外部定义与在循环内部定义vetor并不会带来效率的显著变化(1百万次迭代导致不到0.8s的差距)
  2. 预申请空间,使用赋值而非push_back性能很好
  3. 外部定义+预申请空间+resize+赋值,效果最佳

附录

测试代码如下:

#include <iostream>
#include <vector>
#include <array>
#include <chrono>
#include <random>

using namespace std ;

int main(int argc ,  char *argv[] )
{
    unsigned seed = chrono::high_resolution_clock::now().time_since_epoch().count() ;
    mt19937 rand_generator(seed) ;
    uniform_int_distribution<int> dist(6,666) ;
    const int MaxIteNum = 0x100000 ;

    array<int , MaxIteNum> vec_size ;
    chrono::high_resolution_clock::time_point start_tp  = chrono::high_resolution_clock::now() ;
    for(size_t cnt = 0 ; cnt < MaxIteNum ; ++cnt) 
    {
        vec_size[cnt] = dist(rand_generator) ;
    }
    chrono::duration<double> duration_cost = static_cast<chrono::duration<double>>( chrono::high_resolution_clock::now() - start_tp ) ;
    cout << "generate " << MaxIteNum << " random size , cost " << duration_cost.count() << " s." << endl ;
    // Method 1 , just create in circulation

    start_tp  = chrono::high_resolution_clock::now() ;
    for(size_t cnt = 0 ; cnt < MaxIteNum ; ++cnt)
    {
        vector<int> tmp_vec ;
        int random_size = vec_size[cnt] ;
        for(size_t idx = 0 ; idx < random_size ; ++idx)
            tmp_vec.push_back(idx) ; 
    }
    duration_cost = static_cast<chrono::duration<double>>( chrono::high_resolution_clock::now() - start_tp ) ;
    cout << "Method 1 (ceate at every iteration and no previous space allocation) cost  " << duration_cost.count() << " s."<< endl  ;

    // Method 2 , create in circulation , but do pre-allocation

    start_tp = chrono::high_resolution_clock::now() ;
    for(size_t cnt = 0 ; cnt < MaxIteNum ; ++cnt)
    {
        int random_size = vec_size[cnt] ;
        vector<int> tmp_vec(random_size) ;
        for(size_t idx = 0 ; idx < random_size ; ++idx) 
            tmp_vec[idx] = idx ;
    }
    duration_cost = static_cast<chrono::duration<double>>( chrono::high_resolution_clock::now() - start_tp ) ;
    cout << "Method 2 (create at every iteration but use pre-allocation) cost " << duration_cost.count() << " s."<< endl ;

    // Method 3 , create before circulation & do pre-allocation , but use `resize` to change size 

    start_tp = chrono::high_resolution_clock::now() ;
    vector<int> tmp_vec4method3(666 , 0) ;
    for(size_t cnt = 0 ; cnt < MaxIteNum ; ++cnt)
    {
        int random_size = vec_size[cnt] ;
        tmp_vec4method3.resize(random_size) ;
        for(size_t idx = 0 ; idx < random_size ; ++idx)
            tmp_vec4method3[idx] = idx ;
    }
    duration_cost = static_cast<chrono::duration<double>>( chrono::high_resolution_clock::now() - start_tp ) ;
    cout << "Method 3 (create before circulation & pre-allocatin & use `resize` to set size) cost " << duration_cost.count() << " s."<< endl ;

    // Method 4 , create before circulation & do pre-allocation , use `clear` and push_back

    start_tp = chrono::high_resolution_clock::now() ;
    vector<int> tmp_vec4method4(666 , 0) ;
    for(size_t cnt = 0 ; cnt < MaxIteNum ; ++cnt)
    {
        int random_size = vec_size[cnt] ;
        tmp_vec4method4.clear() ;
        for(size_t idx = 0 ; idx < random_size ; ++idx)
            tmp_vec4method4.push_back(idx) ;
    }
    duration_cost = static_cast<chrono::duration<double>>( chrono::high_resolution_clock::now() - start_tp ) ;
    cout << "Method 4 (create before circulation & pre-allocatin & use `clear` and `push_back`)" << duration_cost.count() << " s."<< endl ;


    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值