C語言堆內存堆申請與文件讀入的性能分析

前言:linux

    對於C語言的學習者來講,對於內存的分析與管理是不得不接觸到的問題。這篇文章我但願來討論下對與C語言對堆內存的使用問題。寫這篇博文的緣由是因爲最近在學習C的過程當中的一個查字典的小項目實戰,項目中提供了一個約22萬個詞彙以及其解釋的詞庫文件,在字典程序中要首先對字典文件進行讀取,解析,設計數據結構,存入內存,並進行排序,並將字典的內存中的二進制形式存入到緩存文件中,在有緩存存的狀況下直接載入緩存,跳過初始化過程。centos

字典文件格式以下(#開頭一行是單詞,Trans:後第二行是解釋,多個解釋以@分割):數組

#abrasion
Trans:n. 磨去;磨損;磨損處
#abrasive
Trans:n. 研磨劑@a. 研磨的

設計的單詞結構體以下:緩存

/* 單詞結構體 */
struct WORD{
	char *key;				// 單詞字段
	int ntrans;				// 單詞解釋個數
	char **trans;                           // 單詞解釋
};
typedef struct WORD word;

    每載入一個單詞,爲單詞malloc一個堆空間,並將首地址存入key指針中,記錄下單詞有幾個解釋,存入ntrans中,並開闢一個對應長度的指針數組給trans,併爲每一個解釋開闢空間,存入指針數組。 由上文敘述,這樣的一個字典程序,初始化的過程有諸多方法,可是這裏關於效率有許問題我感到困惑,這裏接下來討論下(下面全部代碼以CentOS6.5+GCC x64環境下 編譯運行作爲測試的參考環境,全部代碼均親自編寫測試,保證結果然實準確,其餘平臺下不保證代碼正確性和結果的一致,因此來看博客的朋友們,我也是初學者有問題歡迎指正,如結果有所出入的話,歡迎共同探討,勿噴):數據結構


問題:併發

    1. 讀取文件大小有多少字節的方式有兩種,第一種是經過讀取文件的屬性,但這種方式要依賴於操做系統,對於跨平臺來講彷佛有點不是特別可取。另外一種是先將文件指針移動到結尾處,在計算指針的位置,由此思路想到的問題是假設說文件很大,文件指針的移動方式是怎樣的,文件指針移動100和移動100000的性能開銷是相同的嗎?函數

#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
#define N 50				// 每次申請的長度
#define T 104856	                // 申請空間的次數
long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start,temp;
    int i;
    
    start = getTime();
    for(i=0;i<T;i++);
    temp = getTime()-start;
    
    start = getTime();
    
    for(i=0;i<T;i++)
	malloc(N);
    printf("time is : %d\n",getTime()-start-temp);
}

N=1    T=1024*1024    time is : 2
N=2    T=1024*1024    time is : 2
N=3    T=1024*1024    time is : 4
N=5    T=1024*1024    time is : 4
N=10   T=1024*1024    time is : 4
N=50   T=1024*1024    time is : 6
N=100  T=1024*1024    time is : 7
N=500  T=1024*1024    time is : 23
N=1000 T=1024*1024    time is : 41

     以上數據均是屢次測試取平均值得結果,由上數據不難看出對於較小空間的申請,單次申請操做所消耗的時間幾乎沒有任何差異,對於一次性申請較大內存空間所須要消耗的時間變化比較明顯,但並未看出有數量級上的差距,很明顯對於malloc申請內存的時間開銷,會應爲單次申請的空間長度增長而增長,但增長的時間開銷成本並非很大。由此接下來將對每次申請的內存大小作成倍的增加,看看對於申請一個較大的內存空間,採用一次申請和屢次申請的性能開銷差異。性能

N=1024*1024*1    T=1    time is : 0
N=1024*1024*10   T=1    time is : 0
N=1024*1024*100  T=1    time is : 0
N=1024*1024*1000 T=1    time is : 0

     由以上數據綜合以前的分析,malloc函數進行對內存的申請來講,單次申請的過程,時間開銷隨申請的空間大小增長有所增長,但差異卻小到能夠幾乎能夠忽略不計,所以能夠認爲對於一次malloc申請內存空間的時間開銷與所申請的內存空間大小無關,所以對於大內存的申請來講,一次性申請要遠比屢次申請的性能高上不少不少。學習


    2. 文件讀取函數fread()用於文件的批量讀入。一口氣將整個文件讀入內存和一行一行讀入,或者一個字符一個字符的讀取,以此讀取所有內容的性能開銷是否一致。或者說差距是否很大?測試

#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>

long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    char *buffer;
    FILE *dict;				// 詞庫文件句柄
    unsigned int size;	        	// 文件大小
	
    // 打開目標文件
    dict = fopen("dict", "r");
    if(!dict)
        return 0;
	
    // 讀取文件大小,並申請空間
    fseek(dict, 0, SEEK_END);
    size = (unsigned)ftell(dict);
    rewind(dict);
    buffer = (char*)malloc(size);
    
    // 計時開始
    start = getTime();
	
    fread(buffer, 1, size, dict);	// 將文件讀入緩衝區
	
    // 計時結束
    printf("time is : %d\n",getTime()-start);
	
    fclose(dict);
}

執行結果:time is : 3


#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>

long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    char *buffer;
    FILE *dict;				// 詞庫文件句柄
    unsigned int size;	        	// 文件大小
	
    // 打開目標文件
    dict = fopen("dict", "r");          // 這是一個44萬行的字典庫文件
    if(!dict)
        return 0;
	
    // 讀取文件大小,並申請空間
    fseek(dict, 0, SEEK_END);
    size = (unsigned)ftell(dict);
    rewind(dict);
    buffer = (char*)malloc(size);
    
    // 計時開始
    start = getTime();
	
    while(fgets(buffer, size, dict));	// 將文件讀入緩衝區
	
    // 計時結束
    printf("time is : %d\n",getTime()-start);
	
    fclose(dict);
}

執行結果:time is : 13


#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>

long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    char *buffer;
    FILE *dict;				// 詞庫文件句柄
    unsigned int size;	        	// 文件大小
	
    // 打開目標文件
    dict = fopen("dict", "r");
    if(!dict)
        return 0;
	
    // 讀取文件大小,並申請空間
    fseek(dict, 0, SEEK_END);
    size = (unsigned)ftell(dict);
    rewind(dict);
    buffer = (char*)malloc(size);
    
    // 計時開始
    start = getTime();
	
    while(fgetc(dict)!=EOF);	// 將文件讀入緩衝區
	
    // 計時結束
    printf("time is : %d\n",getTime()-start);
	
    fclose(dict);
}

執行結果:time is : 45


     綜上所述,文件讀取時,fread()進行批量讀取的效率遠高於一行一行讀取,單個字符循環讀取的效率是最低的。做爲補充此處對一樣的22萬行數據讀入內存後進行遍歷,以便更直觀的瞭解移動文件指針讀取內容的性能開銷進行對比。

#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>

long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    char *buffer;
    FILE *dict;				// 詞庫文件句柄
    unsigned int size;	        	// 文件大小
	
    // 打開目標文件
    dict = fopen("dict", "r");
    if(!dict)
	return 0;
	
    // 讀取文件大小,並申請空間
    fseek(dict, 0, SEEK_END);
    size = (unsigned)ftell(dict);
    rewind(dict);
    buffer = (char*)malloc(size);
    fread(buffer, 1, size, dict);	// 將文件讀入緩衝區
	
    // 計時開始
    start = getTime();
	
    while(*buffer++ != EOF);

    // 計時結束
    printf("time is : %d\n",getTime()-start);
	
    fclose(dict);
}

執行結果:time is : 16

    綜上,對於須要將文件內容逐字符獲取的狀況來講,先將整個文件fread讀入內存,再進行遍歷的執行效率要好的多。但細心的讀者也應該想到了,對於先將文件內容所有讀入內存再進行遍歷的方式,前提是要有足夠的內存,若是文件很是的大的狀況下,對內存的開銷,即便是使用完後立刻釋放,在那一瞬間也十分巨大。此外並不是任何場景下對文件的內容讀取後都有駐留內存的須要,而這種時候逐字符讀取的空間消耗是最低的。不過在本文的前提下,明顯與載入文件內容的性能要好的多。對於一次性讀取一行的方式在此時的性能開銷與一口氣讀入再遍歷的開銷接近,對於須要逐行讀取並遍歷的狀況彷佛性價比挺高,這裏就不具體分析。不過這裏有個未能解決的小困惑是,筆者這裏的用的是固態硬盤的設備,若這裏與磁盤IO速度關係較大的話,在機械硬盤上也許看似並不明顯的速度差也許會更明顯。可是總之若是在內存充足的狀況下,先將文件批量讀入的效率必定會高於其餘方式,磁盤IO速度越慢恐怕差距會越明顯,對於空間換時間的方式,如何選擇,就看具體情形吧。


    3. 對於一個打開了的文件,文件指針的移動距離是否與性能開銷有關?

#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>

long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    int i;
    FILE *dict;	
	
    // 打開目標文件
    dict = fopen("dict", "r");
    if(!dict)
	return 0;
	
    // 計時開始
    start = getTime();
	
    for(i=0;i<1000000;i++){
	fseek(dict, 0, SEEK_END);
	rewind(dict);
    }

    // 計時結束
    printf("time is : %d\n",getTime()-start);
	
	fclose(dict);
}

執行結果: time is : 700


#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>

long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    int i;
    FILE *dict;	
	
    // 打開目標文件
    dict = fopen("dict", "r");
    if(!dict)
	return 0;
	
    // 計時開始
    start = getTime();
	
    for(i=0;i<1000000;i++){
	fseek(dict, 1, SEEK_SET);
	rewind(dict);
    }

    // 計時結束
    printf("time is : %d\n",getTime()-start);
	
	fclose(dict);
}

執行結果: time is : 220

    這裏說明下上面兩段代碼的執行結果其實並不是一個確切的指,因爲計算機在執行的時候有不少因素,在屢次執行的結果是不同的,以上給出的220和700是我根據屢次執行的結果求平均給出的大概值,220指的是每次的執行結果大部分都落在220這個數字附近,執行時間爲220這個數量級,這裏使用的文件一樣爲那個22萬行左右的字典文件。由上面兩段測試代碼能夠看出來,第一段代碼是將文件指針移動到文件尾部再移動回來,重複1000000次,第二段代碼是然文件指針移從開頭向後移動一個位置再移動回來,一樣重複1000000次,這兩種作法產生了大約三倍的時間差,所以若是咱們猜想說文件指針的移動距離同移動效率無關顯然是不正確的,由此,進一步進行下列測試:

#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
#define N 10000
long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    int i;
    FILE *dict;	
	
    // 打開目標文件
    dict = fopen("dict", "r");
    if(!dict)
	return 0;
	
    // 計時開始
    start = getTime();
	
    for(i=0;i<1000000;i++){
	fseek(dict, N, SEEK_SET);
	rewind(dict);
    }

    // 計時結束
    printf("time is : %d\n",getTime()-start);
	
    fclose(dict);
}

N=1     time is : 220
N=10    time is : 220
N=100   time is : 220
N=1000  time is : 220
N=10000 time is : 480

     由上面的這組數據發現一個很奇怪的現象,當N在1-1000的時候,測試結果幾乎都落在220左右這個數量級上。與以前得出的結論,移動距離與效率有影響發生了矛盾,而到10000時,時間開銷忽然變化到480這個數量級附近,產生了兩倍多的時間差。因而這是後產生了一個猜測,文件指針的移動是否會和分頁機制有關,在同頁面內移動文件指針的開銷是相同的,而每次跨頁的過程則要產生額外的性能開銷,爲此執行下面兩組用例再進行一次測試。

N=4096  time is : 220
N=4097  time is : 420

    很明顯,因爲一個分頁通常默認是4K,4096未發生跨頁,時間開銷與以前同樣,都在220這個數量級內。而4097落到了第二頁,性能開銷明顯馬上落到了另外一個數量級。由此能夠發現文件指針的移動是基於分頁機制進行的。


    4. ftell的效率是否和文件指針的位置有關?

#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
#define N 1
long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    int i;
    FILE *dict;	
	
    // 打開目標文件
    dict = fopen("dict", "r");
    if(!dict)
	return 0;
    fseek(dict, N, SEEK_SET);
	
    // 計時開始
    start = getTime();
	
    for(i=0;i<1000000;i++)
	ftell(dict);

    // 計時結束
    printf("time is : %d\n",getTime()-start);
	
    fclose(dict);
}

N=1     time is : 16
N=10    time is : 16
N=100   time is : 16
N=1000  time is : 16
N=10000 time is : 16

    由此能夠看出查看指針爲位置的時間開銷與指針當前位置無關,且開銷極低,與文件大小無關。

 

   4. 關於realloc函數,據瞭解,用法是若是當前空間後還有空餘的空間,則直接繼續分配空間給他,若不存在空餘空間,則從新malloc新的空間,並將原來的內容拷貝進去。因而可知如果後者狀況下realloc的性能開銷成本顯然很大。那麼realloc在什麼狀況下分配的內容後面會有東西,何時是空餘空間?在單線程的狀況下,最新malloc的地址,是否只要沒有再malloc過新的內容,後面的內存空間就必定爲閒置內存?

#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    int i;
    char *suffer;	
    suffer = malloc(1);
	
    // 計時開始
    start = getTime();
	
    for(i=2;i<1000002;i++)
	suffer = malloc(1);
	
    // 計時結束
    printf("time is : %d\n",getTime()-start);
}

time is : 36


#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    int i;
    char *suffer;	
    suffer = malloc(1);
	
    // 計時開始
    start = getTime();
	
    for(i=2;i<1000002;i++)
	suffer = realloc(suffer,i);
	
    // 計時結束
    printf("time is : %d\n",getTime()-start);
}

time is : 10

     由上面看出,經過realloc申請到1000001字節內存地址的時間開銷相比於malloc執行1000000次,明顯要快了很是多,所以對於不定長空間的動態申請來講,經過realloc來進行要快不少。可是不難發現,在上面的代碼範例中,因爲以前瞭解到relloc存在兩種狀況,第一種是當前地址空間以後還有足夠的空餘空間,由此只要對已申請到的空間進行擴展就好了,不然須要新申請並拷貝。這裏顯然是前者,若是是後者的狀況,realloc的效率應該很慢猜對。由此能夠猜想,malloc的申請規則,是連續分配的,每申請一個空間,就接着以前申請空間進行申請,所以對於上面代碼中,未在suffer以後申請新的空間,所以suffer的後面始終都是空餘空間,由此爲了驗證上述猜測,設計了下面這段代碼進行驗證分析:

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
    char *buffer_0,*buffer_1;	
    buffer_0 = malloc(1);
    buffer_1 = malloc(1);
    printf("buffer_0 = %p\n",buffer_0);
    printf("buffer_1 = %p\n",buffer_1);
    buffer_0 = realloc(buffer_0,2);
    printf("buffer_0 = %p\n",buffer_0);
}

buffer_0 = 0x22f1010
buffer_1 = 0x22f1030
buffer_0 = 0x22f1010

    從打印出來的地址能夠看出,先爲buffer_0分配1字節到地址0x22f01010,再爲buffer_1分配的時候,buffer_1的地址確實是在buffer_0以後,卻不是0x22f01011,而是間隔了32個字節。在爲buffer_0進行realloc的時候,因爲剛剛觀察過內存地址的結構,在buffer_0以後並非牢牢跟着buffer_1的,而是間隔了32字節,有31字節的空餘,因此天然的會想到以前使用過的規則,在地址後有足夠的空閒空間,因而直接對地址進行擴充便可。

    由上述現象進行分析,有了解過結構體的內存存儲結構的朋友,我想此時應該和我同樣都會想到了C語言在處理結構體內存中存儲方式時,因爲讀取效率的考慮所引入的字節對齊機制。由此猜測,C語言,或者說是操做系統在處理內存申請的時候爲了提升性能應該也採用了字節對齊的機制,而這裏的測試來看應該是32字節對齊,對申請不滿32字節的申請對齊到32字節的位置進行內存分配,若是此時進行了realloc,realloc的長度小於32,也就是以後必定會有足夠的空閒空間,或者當前地址對應的空間申請到的是最末端的地址空間。以及同時還有個疑問,假設對realloc的長度超過32,且非末端地址,也就是須要在未使用的地址中從新開闢一個大空間,那麼原來該地址的空間應該空餘了出來,此時再進行malloc新變量是繼續向後存放,仍是直接對閒置出的內存碎片進行利用?爲了驗證上述猜測,繼續測試下面這段代碼:

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
    char *buffer_0,*buffer_1,*buffer_2;	
	buffer_0 = malloc(1);
	buffer_1 = malloc(1);
	printf("buffer_0 = %p\n",buffer_0);
	printf("buffer_1 = %p\n",buffer_1);
	buffer_0 = realloc(buffer_0,33);
	printf("buffer_0 = %p\n",buffer_0);
	buffer_2 = malloc(1);
	printf("buffer_2 = %p\n",buffer_2);
}

buffer_0 = 0x2569010
buffer_1 = 0x2569030
buffer_0 = 0x2569050
buffer_2 = 0x2569010

    分析上面代碼和執行結果,能夠發現,以前的猜測顯然是正確的,buffer_0一開始獲得的地址是0x2569010,buffer_1獲得的地址是0x2569030,按照32字節對齊的規律分配。接着對buffer_0進行realloc了33字節,在原地址擴充顯然不夠,因而從新到下一個32字節對齊位置,也就是0x2569050進行存放,此時爲buffer_2進行分配,buffer_2獲得的地址和buffer_0一開始的地址相同。爲此,顯然在realloc的時候buffer_0的地址進行了更改,同時釋放了原來佔有的空間,且這出於低地址被釋放的內存空間在新變量申請的時候會被優先分配。不過順帶提下,在寫這篇博文的同時,筆者的一個朋友順手測了下win32平臺下的狀況彷佛有很多區別,這裏使用的centos6.5+gcc環境下,不論測試多少次都嚴格遵循32字節對齊的結論,而win32下彷佛也存在對齊現象,兩次內存申請的首地址以前存在空閒空間,但這個空間長度並不是32,也不是某個確切數值,乍看之下並未看出很明顯的規律性。對於細心的讀者應該也看出對於上面代碼的執行結果,每次執行的時候,即使都是第一次對堆內存進行申請,可是申請到的內存地址只能說大體都是落在0x2000000這個數量級上,並不能很明確說有某一個確切的起始點,而win32下每次都是一個確切起始值。對此這裏不對win32進行詳細分析,只是說明下現象。感謝這位朋友提供的測試結果。


    5. 由上一個問題的分析,對malloc和realloc在分配內存的問題基本上有了比較清楚的問題。此時又回到了效率的問題上,以前的測試過的realloc與malloc的效率對比,若是考慮了字節對齊這個概念,那麼得出的,只要不從新分配拷貝的前提下,從新分配realloc這個結論是否會受字節對齊而改變。而對於發生地址變換拷貝內容的realloc效率低於直接malloc新地址是毋庸置疑的事實,可是對於此時的效率會差距達到怎樣的數量級?

#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    int i;
    char *suffer;	
    suffer = malloc(33);
	
    // 計時開始
    start = getTime();
	
    for(i=66;i<33000000;i+=33)
	suffer = malloc(33);
	
    // 計時結束
    printf("time is : %d\n",getTime()-start);
}

time is : 41


#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    int i;
    char *suffer;	
    suffer = malloc(33);
	
    // 計時開始
    start = getTime();
	
    for(i=66;i<33000000;i+=33)
	suffer = realloc(suffer,i);
	
    // 計時結束
    printf("time is : %d\n",getTime()-start);
}

time is : 12

    這裏簡化掉其餘更多組相同方式的測試數據和結果,只給出一組較具備表明性的代碼。對於彷佛已經可以很好的說明,以前猜測的對齊機制並不會對malloc和realloc的效率構成影響。無論每次請求的空間大小是大於對齊的基數(這裏指linux下)仍是小於,時間成本開銷都是同樣的。接着下面的測試代碼對進行了地址遷移的realloc和普通的malloc的性能開銷作一個對比分析:

#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    int i;
    char *buffer[1000000];	
	
    // 計時開始
    start = getTime();
    for(i=0;i<1000000;i++)
	buffer[i] = malloc(33);
	
    // 計時結束
    printf("time is : %d\n",getTime()-start);
}
time is : 44

#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
#define N 1
long long getTime() {
    struct timeb t;
    ftime(&t);
    return 1000 * t.time + t.millitm;
}
int main(int argc, char **argv){
    long long start;
    int i;
    char *buffer[1000000];	
	
    for(i=0;i<1000000;i++)
	buffer[i] = malloc(1);
	
    // 計時開始
    start = getTime();
    for(i=0;i<1000000;i++)
	buffer[i] = realloc(buffer[i],N);
	
    // 計時結束
    printf("time is : %d\n",getTime()-start);
}
N=1      time is : 16
N=33     time is : 47
N=50     time is : 53
N=64     time is : 58
N=65     time is : 60
N=100    time is : 70
N=300    time is : 137
N=400    time is : 250
N=420    time is : 400
N=450    time is : 1000
N=500    time is : 1187
N=1000   time is : 12411

    由上述測試結果能夠看出,對於發生後續空餘空間不足的realloc行爲的性能開銷果真如預期的同樣,對於起始地址較小,而再分配的地址也較小的狀況下,從新分配和拷貝的性能成本都較低,和直接malloc的成本開銷起始差異不大,後者說僅僅只是稍大一些,幾乎能夠忽略不計,可是隨着reallloc空間的增長,時間開銷在起初變化較小,但逐漸的成指數增加,所以能夠得出結論,若發生重分配的realloc在重分配大地址時的性能開銷是巨大的。並且這裏其實地址都只是一個字符,內容拷貝的成本應當至關低,若起始內容較大,拷貝成本必然也會顯著增長,所以不難發現對於大內容的realloc,若是沒法確保realloc的地址出於最末位地址,應當儘可能避免該方式進行內存重申請的使用,一旦遇到大併發的應用場景,或者大量調用的時候,性能損耗將會十分明顯。

相關文章
相關標籤/搜索