容器(vector)、数组、new创建的动态数组,你到底用哪一个(执行效率分析)

1.问题的提出

在没有了解vector之前,动态数组一般都是又new创建的。在了解vector后发现vector竟是那样方便好用,但方便的同时却是以牺牲执行效率为代价的。网上对vector和array的评价和吐槽,也是喜忧参半,各有不同啊。在面临选择的时候,我们到底用哪一种呢,我们可能都犹豫过?下面对该问题进行理论分析和实际测试验证。

2.理论分析

2.1预备知识-程序的内存分配

一个由C/C++编译的程序占用的内存分为以下几个部分

       1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。是一种线性结构,其操作方式类似于数据结构中的栈,操作速度较快。但程序员是无法控制。

       2 、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,是一种链式结构,分配方式倒是类似于链表,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

        3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 

        4、文字常量区—常量字符串就是放在这里的。 程序结束后由系统释放

        5、程序代码区—存放函数体的二进制代码。

其中栈和堆构成了动态数据区:

├———————┤低端内存区域 

│ …… │ 

├———————┤ 

│ 动态数据区 │ 

├———————┤ 

│ …… │ 

├———————┤ 

│ 代码区 │ 

├———————┤ 

│ 静态数据区 │ 

├———————┤ 

│ …… │ 

├———————┤高端内存区域

以上堆和栈的分析参考博客1,关于堆和栈的区别与联系更详细的分析,请点击博客1

2.2数组、动态数组和vector的讨论如下:

回到本文的主题,数组是底层数据类型,存放在栈中,其内存的分配和释放完全由系统自动完成,效率最高;动态数组是程序员由new运算符创建的,存放在堆中,需由delete运算符人工释放,否则会内存泄露;vector,存放在堆中,由STL库中程序负责内存的分配和释放,使用方便。

关于vector的分析,参考STL源码剖析(P119-vector的内存管理)和C++primer(第四版9.4vector容器的自增长),vector的构造和内存管理如图1所示。如果容器中已经没有空间容纳新的元素,此时,由于元素必须连续存储以便索引访问,所以不能在内存中随便找个地方存储这个新元素。于是,vector 必须重新分配存储空间,用来存放原来的元素以及新添加的元素:存放在旧存储空间中的元素被复制到新存储空间里,接着插入新元素,最后撤销旧的存储空间。这是vector效率低的主要原因。

注意所谓的动态自增加,并不是在原空间之后接续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块大空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了。注意size和capacity的区别,size指容器当前拥有的元素个数,capacity指容器在必须分配新存储空间之前可以存储的元素总数,capacity总是大于或等于size的。

容器(vector)、数组、new创建的动态数组,你到底用哪一个(执行效率分析)

图1(来自STL源码剖析,图4-2)

3.数组、动态数组、vector的测试

对vector、预先reverse的vector、数组、new创建的动态数组测试代码如下:

vector的测试代码:

		DWORD start=GetTickCount();
int t=100;
int n=200000;
while (t)
{
vector<int> a,b;
for (int i=0;i<n;i++)
a.push_back(i);
t--;
}
cout<<"Runing time of program:"<<GetTickCount()-start<<endl;

预先reverse的vector的测试代码:

		 start=GetTickCount();
t=100;
n=200000;
while (t)
{
vector<int> a,b;
b.reserve(n+1);
for (int i=0;i<n;i++)
b.push_back(i);
t--;
}
cout<<"Runing time of program:"<<GetTickCount()-start<<endl;

数组的测试代码:

		 start=GetTickCount();
t=100;
n=200000;
while (t)
{
int a[200000];
for (int i=0;i<n;i++)
a[i]=i;
t--;
}
cout<<"Runing time of program:"<<GetTickCount()-start<<endl;

new创建的动态数组:

		 start=GetTickCount();
t=100;
n=200000;
while (t)
{
int *p=new int[n+1];
for (int i=0;i<n;i++)
p[i]=i;
delete []p;
t--;
}
cout<<"Runing time of program:"<<GetTickCount()-start<<endl;

运行结果为:

运行结果从上到下依次为,vector,预先reverse的vector、数组和动态数组的以上代码的执行时间(单位ms),从中可以看出,数组的执行效率是vector的10倍左右。执行效率不在同一个数量级,虽然容器操作简单方便。其效率排序依次是数组>动态数组>预先reverse的vector>vector。总之,数组和容器各有千秋,若硬件速度够快,对时间要求不那么高,用容器方便,也未尝不可。关键看你,看中什么,是效率还是方便。

容器(vector)、数组、new创建的动态数组,你到底用哪一个(执行效率分析)

上一篇:Docker容器技术-第一个容器


下一篇:JAVA基础学习——java数组002