容器(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的。


                                                  圖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。總之,數組和容器各有千秋,若硬件速度夠快,對時間要求不那麼高,用容器方便,也未嘗不可。關鍵看你,看中什麼,是效率還是方便。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章