胡平平,王晶杰
(北京信息科技大學(xué) 自動(dòng)化學(xué)院,北京100192)
傳統(tǒng)的動(dòng)態(tài)存儲(chǔ)管理 (DMM)有首先適配法、再次適配法、最佳適配法和最壞適配法4種方法[1],許多文獻(xiàn)也提出了各種改進(jìn)算法以提高DMM 的性能,如利用二叉樹來管理內(nèi)存分區(qū)以提高DMM 的效率[2];利用虛擬內(nèi)存管理實(shí)現(xiàn)DMM,以便有效降低時(shí)間耗費(fèi)[3];改進(jìn)DMM 算法降低分配失敗率[4]、或改善內(nèi)存泄漏[5]、或改進(jìn)動(dòng)態(tài)內(nèi)存分配的脆弱性并提高內(nèi)存分配安全性[6]等。有些研究者則注重不同應(yīng)用對(duì)DMM 需求的差異,希望找到其需求特性以提供最佳策略[7-9]。文獻(xiàn) [10]通過模擬應(yīng)用的內(nèi)存分配需求,從所提供的多種分配方法中選擇最適合的,以便在存儲(chǔ)區(qū)使用量和處理功耗方面取得好的效果。這些改進(jìn)措施可分為兩類:一類是在系統(tǒng)級(jí)對(duì)DMM 進(jìn)行改進(jìn),使其對(duì)各種動(dòng)態(tài)存儲(chǔ)的需求都具有較優(yōu)的性能;另一類則針對(duì)具體應(yīng)用的特性,分別提供不同的最佳方法。
筆者發(fā)現(xiàn),在需要連續(xù)存儲(chǔ)大量大小和數(shù)量均不確定的數(shù)據(jù)時(shí),若直接使用系統(tǒng)的DMM 實(shí)現(xiàn),其空間利用率和執(zhí)行效率都比較差。為此,本文提出了動(dòng)態(tài)存儲(chǔ)再分配(DMRA)方案,該方案有效地減少了對(duì)系統(tǒng)DMM 的執(zhí)行次數(shù),不僅明顯提高了存儲(chǔ)空間的利用率,還極大地提高了存儲(chǔ)空間的分配速度。
本文以VC++6.0控制臺(tái)應(yīng)用程序?yàn)槔?,先分析指出了常?guī)動(dòng)態(tài)存儲(chǔ)分配存在的問題,然后介紹了DMRA 方案和實(shí)現(xiàn)算法,在對(duì)其性能進(jìn)行了簡單的理論分析之后,用多個(gè)實(shí)例數(shù)據(jù)進(jìn)行了測試,最后給出了改進(jìn)方案和注意事項(xiàng)。實(shí)例數(shù)據(jù)的測試結(jié)果表明該方案能節(jié)省36%至75%的存儲(chǔ)空間,而分配速度則提高了20~50倍。
C語言實(shí)現(xiàn)DMM 的基本函數(shù)是malloc(),VC++系統(tǒng)又為其提供了DEBUG (以下簡稱D 版)和RELEASE(以下簡稱R 版)兩種不同版本的代碼。malloc()每分配一塊存儲(chǔ)空間,都要使用一些額外的空間以實(shí)現(xiàn)DMM,兩種版本代碼所使用的額外空間和執(zhí)行時(shí)間不同,因此其效率也不相同。如要分配長度是L 字節(jié)的空間,實(shí)際使用的存儲(chǔ)空間長度AL 為
兩種版本的BaseL 都是8 (實(shí)際上是鏈表兩個(gè)指針占用的空間),R 版的ExtraL 為0,D 版的ExtraL 是36 (實(shí)際上是一個(gè)32字節(jié)的信息頭和4字節(jié)的安全預(yù)留),即:
D 版
R 版
令WL=AL-L,這就是每次內(nèi)存分配的浪費(fèi)空間,D版的WL 在44 (L%16=12時(shí))至59 (L%16=13時(shí))之間;而R 版的WL 在8 (L%16=0 時(shí))至23 (L%16=1時(shí))之間。顯然,在L 較小時(shí)WL 與L 相比還是很可觀的,若用malloc()為大量小長度數(shù)據(jù)申請(qǐng)動(dòng)態(tài)存儲(chǔ)空間,則會(huì)導(dǎo)致系統(tǒng)存儲(chǔ)空間的巨大浪費(fèi)。
在比較不同處理方法的執(zhí)行時(shí)間時(shí),處理所執(zhí)行的CPU 指令數(shù)量是較可靠的依據(jù)。由于本文僅涉及動(dòng)態(tài)儲(chǔ)存連續(xù)分配的情況 (即分配結(jié)束前不釋放任何所分配的空間),因此下面僅對(duì)malloc()函數(shù)被連續(xù)調(diào)用的情況進(jìn)行分析。跟蹤分析發(fā)現(xiàn),malloc()代碼的執(zhí)行由下面3部分構(gòu)成
其中,BaseI是固定執(zhí)行的指令數(shù)量,ScanI 是執(zhí)行串掃描指令的重復(fù)次數(shù),StosI 是執(zhí)行串存儲(chǔ)指令的重復(fù)次數(shù)。malloc()對(duì)存儲(chǔ)區(qū)的處理是以4KB 為單位進(jìn)行的,其過程按當(dāng)前要分配存儲(chǔ)區(qū)長度的不同可分兩種情況 (用圖1來說明)。圖中每個(gè)粗矩形框表示一個(gè)4KB 存儲(chǔ)區(qū)域,左邊為低地址,陰影部分為已分配的空間,P0為當(dāng)前可分配空間的起始位置,B1和B2分別為P0后面兩個(gè)4KB 存儲(chǔ)區(qū)的邊界位置。
圖1 malloc()對(duì)存儲(chǔ)空間的處理方法
情況1:分配空間的結(jié)束位置為P1,也即可與P0在同一個(gè)4KB內(nèi)完成分配。ScanI的處理是掃描P0至B1之間是否為0xFEEEFEEE (以下簡稱DD1),StosI 的處理分3部分:第一部分是將P1至B1之間填充為DD1,第二部分是將P0至P1之間填充為0xBAADF00D,第三部分 (僅D版)是將P0至P1之間實(shí)際申請(qǐng)長度的空間填充為0xCDCDCDCD。
情況2:分配空間的結(jié)束位置為B1之后的P2,即不能與P0在同一個(gè)4KB內(nèi)完成分配。ScanI的處理有兩部分,StosI的處理則有4部分,依次是:掃描P0至B1之間是否為DD1,將P0至B2之間填充為DD1,掃描P0至B2之間是否為DD1,將P2至B2之間填充為DD1,將P0至P2之間填充為0xBAADF00D,(僅D 版)將P0至P2之間實(shí)際申請(qǐng)長度的空間填充為0xCDCDCDCD。
上述串指令的重復(fù)次數(shù)不僅和P1的位置有關(guān),還和申請(qǐng)長度有關(guān),本文涉及的分配長度有兩類,分別為1至255(平均長15至25)和固定長度2048,因此B1和B2一定是相鄰的兩個(gè)4KB區(qū)域。下面以第一類情況1的D 版為例,說明其平均指令數(shù)量的求解方法。
首先固定執(zhí)行的指令數(shù)量BaseI=716;掃描區(qū)域最短8,最長4KB-8,平均長2KB,按雙字為單位掃描,掃描指令平均重復(fù)次數(shù)為512;同理,第一次填充指令的平均重復(fù)次數(shù)也為512;第二次填充區(qū)域長度為申請(qǐng)長度與16B對(duì)齊后加0x30,最短64,最長304,平均184,平均重復(fù)次數(shù)為46;第三次填充區(qū)域最短4,最長256,平均長130,平均重復(fù)次數(shù)為32。因此,申請(qǐng)長度為1至255情況1的D版malloc()所執(zhí)行指令的平均總數(shù)量為2076。同理可以計(jì)算出情況2的D 版執(zhí)行指令的平均總數(shù)量為3560。至于情況1和情況2出現(xiàn)的概率應(yīng)該按平均申請(qǐng)長度15至25來計(jì)算,其實(shí)際使用空間的長度是72。因?yàn)閮?chǔ)存分配的間隔是8的倍數(shù),按均勻分布在4 KB 范圍內(nèi)計(jì)算,則共有1024種情況,其中只有72/8=9 種會(huì)導(dǎo)致情況2。因此,申請(qǐng)長度為1至255的D 版malloc()執(zhí)行指令總數(shù)量的平均為:2076· (1024-9)/1024+3560·9/1024=2089。同樣方法可以計(jì)算出其它情況下malloc()函數(shù)執(zhí)行的指令數(shù)量,見表1。
表1 各種情況下malloc()函數(shù)執(zhí)行指令的數(shù)量
DMM 之所以需要額外的存儲(chǔ)空間并執(zhí)行相對(duì)復(fù)雜的操作,是為了較好地滿足復(fù)雜的儲(chǔ)存申請(qǐng)和釋放需求。但在實(shí)際應(yīng)用程序中,經(jīng)常需要連續(xù)動(dòng)態(tài)存儲(chǔ)大量的短長度數(shù)據(jù),且在完成所有數(shù)據(jù)的儲(chǔ)存之前,不需要釋放任何空間。在這種情況下,若用malloc ()為每一個(gè)數(shù)據(jù)申請(qǐng)空間,不僅會(huì)導(dǎo)致系統(tǒng)存儲(chǔ)空間的巨大浪費(fèi),而且還執(zhí)行了大量沒必要的操作。為解決這個(gè)問題,本文提出了一種動(dòng)態(tài)儲(chǔ)存再分配方案,可以有效改善該情況下動(dòng)態(tài)儲(chǔ)存的空間利用率和執(zhí)行效率。
所謂動(dòng)態(tài)儲(chǔ)存再分配方案是將零散的動(dòng)態(tài)儲(chǔ)存申請(qǐng)轉(zhuǎn)換為集中申請(qǐng),并利用簡單的本地分配策略對(duì)已申請(qǐng)的存儲(chǔ)空間進(jìn)行有效的再分配。現(xiàn)以短數(shù)據(jù)連續(xù)動(dòng)態(tài)儲(chǔ)存為例加以說明。
圖2是用malloc()連續(xù)為N 個(gè)短數(shù)據(jù)單獨(dú)申請(qǐng)存儲(chǔ)空間的分布情況。其中P1至PN為申請(qǐng)到的N 個(gè)數(shù)據(jù)儲(chǔ)存區(qū)域的指針,每個(gè)實(shí)箭頭線表示一次對(duì)malloc ()函數(shù)的調(diào)用,得到的存儲(chǔ)區(qū)域用粗線矩形表示,其陰影部分為DMM 占用的額外空間。
圖2 為N 個(gè)短數(shù)據(jù)單獨(dú)申請(qǐng)存儲(chǔ)空間的分布
圖3 DMRA 方法為N 個(gè)短數(shù)據(jù)申請(qǐng)存儲(chǔ)空間的分布
從上圖可以看出,DMRA 方案具有更高的存儲(chǔ)空間利用率和較少的malloc()調(diào)用次數(shù),而本地分配處理非常簡單,因此DMRA 將具有更快的速度。
DMRA 的實(shí)現(xiàn)要在滿足應(yīng)用需求的前提下保證其執(zhí)行速度。由于數(shù)據(jù)的數(shù)量N 不確定,因此存儲(chǔ)塊的數(shù)量M 也不確定,指針BPi必須用動(dòng)態(tài)數(shù)組儲(chǔ)存。雖然C++提供了vector模板類可以方便地實(shí)現(xiàn)動(dòng)態(tài)數(shù)組,但其效率非常低,因此本文直接利用realloc ()實(shí)現(xiàn)BPi的動(dòng)態(tài)儲(chǔ)存。為了使用方便,DMRA 僅需實(shí)現(xiàn)一個(gè)和malloc()完全相同函數(shù),其原型為:
void*DMRAalloc(size_t size);
實(shí)現(xiàn)DMRA 算法需要設(shè)置的常量和變量以及各變量的初始值如下所示:
#define INC_BLK_NUM 128 //Bpi動(dòng)態(tài)數(shù)組遞增長度
#define BLK_LEN 2048 //每個(gè)存儲(chǔ)塊的長度
char**pBP=NULL; //Bpi動(dòng)態(tài)數(shù)組指針
int BPLen=0; //Bpi動(dòng)態(tài)數(shù)組長度
調(diào)查顯示,創(chuàng)業(yè)的動(dòng)機(jī)包括獲得財(cái)富、自我實(shí)現(xiàn)、親友支持、社會(huì)支持,創(chuàng)業(yè)動(dòng)機(jī)與少數(shù)民族大學(xué)生創(chuàng)業(yè)能力正相關(guān),相關(guān)系數(shù)為0.72,其中,自我價(jià)值實(shí)現(xiàn)與創(chuàng)業(yè)力相關(guān)性最顯著。
char*CntMemBuf; //當(dāng)前存儲(chǔ)塊首地址
int BlkIdx=-1; //當(dāng)前使用的存儲(chǔ)塊下標(biāo)
int NextPos=BLK _LEN; //當(dāng)前存儲(chǔ)塊下一個(gè)地址
基本DMRAalloc()函數(shù)的實(shí)現(xiàn)流程如圖4所示,pBf是該函數(shù)僅有的一個(gè)局部指針變量。從各變量的初始值可以看出,第一次調(diào)用該函數(shù)將自動(dòng)調(diào)用malloc ()申請(qǐng)第一個(gè)存儲(chǔ)塊,同時(shí)也將第一次調(diào)用realloc()為存儲(chǔ)塊指針分配空間。而本地分配處理則簡單到僅有兩次加法賦值和一次判斷。
圖4 基本DMRA 算法的流程
為了保證DMRA 算法的正確性及較高的空間利用率,必須注意下述設(shè)計(jì)要點(diǎn):
(1)BLK_LEN 必須大于等于所申請(qǐng)存儲(chǔ)空間的最大長度。
(2)DMRA 算法的空間利用率和BLK_LEN 與平均申請(qǐng)長度的比成正比。
(3)每個(gè)存儲(chǔ)塊后部的剩余空間沒有使用,最壞情況下,該剩余空間是最大申請(qǐng)長度減一。
(4)在總申請(qǐng)空間小于存儲(chǔ)塊長度情況下,DMRA 的空間利用率反而會(huì)比直接用DMM 低。
(5)DMRA 算法沒有考慮存儲(chǔ)空間起始地址的對(duì)齊問題。實(shí)際上,若要與A=2k對(duì)齊,只需將NextPos+=size改為NextPos= (NextPos+size+A-1)&~(A-1),將NextPos=size改為NextPos= (size+A-1)&~(A-1)即可。
(6)當(dāng)不再使用由DMRA 申請(qǐng)的存儲(chǔ)空間時(shí),必須將動(dòng)態(tài)數(shù)組pBP中的BlkIdx+1個(gè)指針都釋放掉。為了盡量保證系統(tǒng)存儲(chǔ)空間的連續(xù)性,建議按倒序的順序釋放,最后還要釋放指針pBP。
基本DMRA 算法只能完成一次多存儲(chǔ)區(qū)的連續(xù)分配,在實(shí)際應(yīng)用中,當(dāng)處理完這些數(shù)據(jù)后,常需對(duì)同類型的其它數(shù)據(jù)進(jìn)行相同的處理。雖然可以將DMRA 所申請(qǐng)的存儲(chǔ)塊釋放后重新初始化變量再次使用,但這樣沒有充分利用DMRA 執(zhí)行效率的優(yōu)勢。改進(jìn)的DMRA 算法不僅能實(shí)現(xiàn)多次連續(xù)分配,而且使后續(xù)分配具有更高的執(zhí)行效率。其原理是:不釋放DMRA 已申請(qǐng)的存儲(chǔ)區(qū),僅通過變量的重新設(shè)置,實(shí)現(xiàn)多次連續(xù)存儲(chǔ)DMRA 已申請(qǐng)空間的再使用。為此,僅需再設(shè)置一個(gè)表示Bpi動(dòng)態(tài)數(shù)組中已申請(qǐng)存儲(chǔ)塊的數(shù)量BlkNum,其初始值為0,并對(duì)基本DMRA 算法進(jìn)行改進(jìn),如圖5所示。
再次使用DMRA 算法連續(xù)為其它數(shù)據(jù)分配空間前需要進(jìn)行下述設(shè)置:
如果BlkNum 非零 (表示上一次DMRA 至少為一個(gè)數(shù)據(jù)分配了空間),則將BlkIdx和NextPos 清零,并將Cnt-MemBuf設(shè)置為pBP [0];如果BlkNum 為零,則不用改變?nèi)魏巫兞康闹怠_@樣,新數(shù)據(jù)的空間將從上次第一個(gè)存儲(chǔ)塊開始分配。若新數(shù)據(jù)使用的空間沒有上次大,則不需要申請(qǐng)任何新的存儲(chǔ)塊;當(dāng)新數(shù)據(jù)空間比上次大時(shí),DMRA會(huì)繼續(xù)申請(qǐng)新的存儲(chǔ)塊,直到滿足需求為止。
跟蹤測試得出改進(jìn)的DMRA 算法函數(shù)在各種情況下執(zhí)行指令的數(shù)量如表2所示,其中的3473和2679使用了表1的最后一列數(shù)據(jù),且假設(shè)realloc()和malloc()函數(shù)的數(shù)據(jù)一樣。
將表2第二列數(shù)據(jù)與表1第四列數(shù)據(jù)對(duì)比,對(duì)于為長度在1至255的數(shù)據(jù)分配存儲(chǔ)空間時(shí),DMRA 本地分配的執(zhí)行速度分別是直接調(diào)用malloc()的近44 (2089/47)和127 (1781/14)倍,可見DMRA 有非??斓膱?zhí)行速度。
與常規(guī)DMM 性能相比,DMRA 的改進(jìn)程度與許多因素有關(guān),下面以實(shí)際測試的數(shù)據(jù)為例加以說明。本實(shí)例用DMRA 方法為儲(chǔ)存大量隨機(jī)長度的文件名稱字符串分配空間,這些文件可能是一個(gè)磁盤或一個(gè)文件夾中的所有文件。
圖5 DMRA 改進(jìn)算法的流程
表2 改進(jìn)的DMRA 算法函數(shù)各種情況執(zhí)行指令的數(shù)量
WindowsXP系統(tǒng)下文件名稱的長度從1至255不等,數(shù)量從幾百至幾萬不等,非常適合于用本文介紹的DMRA 方法為之分配存儲(chǔ)空間。
測試程序?qū)λ幚淼奈募?shù)量和文件名長度進(jìn)行了統(tǒng)計(jì),按式 (2)和式 (3)計(jì)算出每一個(gè)文件名實(shí)際使用的空間,求和則是直接用malloc ()分配所用的空間。根據(jù)DMRA 處理所使用的存儲(chǔ)塊數(shù)量則可直接算出使用DMRA分配所用的空間。
在執(zhí)行時(shí)間方面,直接分配的時(shí)間是文件數(shù)量乘以單個(gè)malloc()的平均執(zhí)行指令數(shù)量,而DMRA 的時(shí)間則分別是:
D 版
R 版
其中,3500和2703是表2第三列與第二列的差,3488 和2691則是第四列與第三列的差,分別表示為申請(qǐng)存儲(chǔ)塊和動(dòng)態(tài)數(shù)組而額外執(zhí)行的指令數(shù)量。
表3是對(duì)5個(gè)不同對(duì)象的實(shí)際測試數(shù)據(jù),表4則是他們的對(duì)比結(jié)果。圖6和圖7分別是第二和第三個(gè)對(duì)象文件名長度的分布圖 (由測試程序自動(dòng)生成)。
表3 5個(gè)不同對(duì)象的實(shí)際測試數(shù)據(jù)
表4 5個(gè)不同對(duì)象DMRA與直接分配方法的空間和速度比
圖6 C盤文件名長度的分布圖和基本數(shù)據(jù)
圖7 D 盤文件名長度的分布圖和基本數(shù)據(jù)
當(dāng)DMRA 用于多次連續(xù)存儲(chǔ)區(qū)分配時(shí),在無需申請(qǐng)新存儲(chǔ)塊的情況下,與直接分配的速度比是常數(shù),D 版為2089/61=34.25,而R 版則為1781/24=74.21。
表4和表5中第一個(gè)對(duì)象是個(gè)用于測試的數(shù)據(jù),其中僅含不同長度的文件37個(gè),由于僅需一個(gè)存儲(chǔ)塊,且該存儲(chǔ)塊尚有1636個(gè)空間沒有使用,因此其空間利用率最小,R 版下甚至還不如直接分配 (幾乎比直接分配還多用一倍的空間),可見,DMRA 在數(shù)據(jù)量少的情況下,并無優(yōu)勢。后4個(gè)對(duì)象的數(shù)據(jù)量很大,實(shí)際數(shù)據(jù)表明,D 版DMRA 使用的空間才幾乎是直接分配方法的1/4 (24.9% 至33.4%),R 版則是直接分配方法的一半多一些 (53.5%至63.3%)。速度方面,DMRA 在各種情況下都有較大的優(yōu)勢,D 版DMRA 的速度是直接分配方法的23至28倍,R版則是更高的37至50倍。
本文對(duì)需要連續(xù)為大小和數(shù)量均不確定的海量數(shù)據(jù)動(dòng)態(tài)分配存儲(chǔ)空間的問題進(jìn)行了研究,在細(xì)致分析了直接利用malloc()存在的問題后,提出了一種高效的動(dòng)態(tài)存儲(chǔ)再分配方案,并給出了實(shí)現(xiàn)該方案的高效算法。實(shí)際測試結(jié)果表明,該方案有效地減少了系統(tǒng)動(dòng)態(tài)存儲(chǔ)管理的執(zhí)行次數(shù),在明顯節(jié)省系統(tǒng)存儲(chǔ)空間36%至75%的同時(shí),分配速度提高了20~50倍,在重復(fù)多次使用的情況下速度提高最多達(dá)70倍,該方法在作者開發(fā)的多個(gè)以磁盤文件為處理對(duì)象的軟件中得到應(yīng)用,效果明顯。
[1]LI Jun.A brief analysis on dynamic memory management in C/C++program [J].Xinjiang Radio and TV University Journal,2009,13 (2):53-54 (in Chinese).[李軍.淺析C/C++程序運(yùn)行過程中的動(dòng)態(tài)存儲(chǔ)管理 [J].新疆廣播電視大學(xué)學(xué)報(bào),2009,13 (2):53-54.]
[2]HU Bin,SUN Jianli,ZHANG Yongping,et al.Research and implementation of technique for memory management [J].Computer Engineering and Design,2007,28 (5):1226-1228(in Chinese).[胡濱,孫健力,張永平,等.一種內(nèi)存管理技術(shù)的研究與實(shí)現(xiàn) [J].計(jì)算機(jī)工程與設(shè)計(jì),2007,28 (5):1226-1228.]
[3]WEI Haitao,JIANG Yuming,LI Jianwu,et al.Research of high efficient implementation of memory management mechanism [J].Computer Engineering and Design,2009,30 (16):3798-3712 (in Chinese).[魏海濤,姜昱明,李建武,等.內(nèi)存管理機(jī)制的高效實(shí)現(xiàn)研究 [J].計(jì)算機(jī)工程與設(shè)計(jì),2009,30 (16):3798-3712].
[4]Zhao Feimeng,Shu Dong Zhang.Buddy algorithm optimization in linux memory management[J].Applied Mechanics and Materials,2013,2746 (423):746-2750.
[5]Varlese,Marco.Improving performance for dynamic memory allocation[J].Embedded Systems Design,2009,22 (5):21-29.
[6]Yves Younan,Wouter Joosen,F(xiàn)rank Piessens.Improving memory management security for C and C++ [J].International Journal of Secure Software Engineering,2010,1 (2):57-82.
[7]Florence Charreteur, Bernard Botella, Arnaud Gotlieb.Modelling dynamic memory management in constraint-based testing [J].Journal of Systems and Software,2009,82 (11):1755-1766.
[8]Marja del Mar Gallardo,Pedro Merino,David Sanon.Model checking dynamic memory allocation in operating systems[J].Journal of Automated Reasoning,2009,42 (2):229-264.
[9]Jose L Risco-Martin,Manuel Colmenar J,Ignacio Hidalgo J.A methodology to automatically optimize dynamic memory managers applying grammatical evolution [J].The Journal of Systems &Software,2014,91 (1):109-123.
[10]Jose L Risco-Martin,Manuel Colmenar J,David Atienza.Simulation of high-performance memory allocators [J].Microprocessors and Microsystems,2011,35 (8):755-765.