摘要:Glibc庫(kù)是Linux系統(tǒng)最底層的函數(shù)庫(kù),。本文分析了Glibc庫(kù)的函數(shù)構(gòu)成,在龍芯2F平臺(tái)上對(duì)其中的字符串與內(nèi)存的處理,、數(shù)據(jù)轉(zhuǎn)換,、哈希表查找、以及加密函數(shù)的代碼優(yōu)化,。實(shí)驗(yàn)結(jié)果表明,,大部分函數(shù)的優(yōu)化比率達(dá)到了30%以上,對(duì)龍芯2F平臺(tái)的整體運(yùn)行性能提升具有重要意義,。
關(guān)鍵詞:Glibc,;龍芯2F;優(yōu)化
0 引言
龍芯2F是中國(guó)科學(xué)院計(jì)算技術(shù)研究所研制的高性能通用處理器,,具有低功耗、低成本以及自主安全的特點(diǎn),,已應(yīng)用于高性能計(jì)算和日常生活領(lǐng)域,。
龍芯2F以開(kāi)源的Linux作為操作系統(tǒng)。Glibe庫(kù)是Linux系統(tǒng)最底層的運(yùn)行庫(kù),,為其上的應(yīng)用程序提供系統(tǒng)接口以及其它功能函數(shù),。此外,它還是以 C語(yǔ)言" title="C語(yǔ)言">C語(yǔ)言構(gòu)建開(kāi)發(fā)程序時(shí)使用的基本函數(shù)庫(kù),。本文基于龍芯2F體系結(jié)構(gòu)對(duì)Glibc庫(kù)進(jìn)行優(yōu)化,,對(duì)于提升龍芯2F的平臺(tái)性能與用戶(hù)體驗(yàn)有重要意義。
1 Glibc庫(kù)介紹
Glibc庫(kù)是GNU發(fā)布的C運(yùn)行庫(kù),它封裝了Linux操作系統(tǒng)提供的系統(tǒng)服務(wù),,除此之外,,還提供了其它必要的功能服務(wù)實(shí)現(xiàn)。Glibc庫(kù)是Li- nux系統(tǒng)最底層的函數(shù)庫(kù),,是除了操作系統(tǒng)核心以外,,所有應(yīng)用程序賴(lài)以執(zhí)行的基礎(chǔ)環(huán)境。Linux系統(tǒng)上的其它函數(shù)庫(kù)都需要直接或間接依賴(lài)于Glibc 庫(kù),。
Glibc庫(kù)包含兩類(lèi)函數(shù),,一種是與核心溝通的系統(tǒng)函數(shù),它們封裝了系統(tǒng)調(diào)用,,對(duì)傳入?yún)?shù)進(jìn)行預(yù)處理之后就轉(zhuǎn)到系統(tǒng)調(diào)用來(lái)完成功能,,其目的是使得用戶(hù)可以方便地使用操作系統(tǒng)核心提供的服務(wù)。系統(tǒng)調(diào)用接口與Linux操作系統(tǒng)相關(guān),,大部分流程固定,,沒(méi)有太大優(yōu)化余地,對(duì)
其不做處理,。
C庫(kù)中的另一類(lèi)函數(shù)提供常見(jiàn)的通用功能實(shí)現(xiàn),,包括標(biāo)準(zhǔn)輸入輸出控制,字符串處理,,正則表達(dá)式,,字符串加密,查找與排序等,。它們提供基本的,、與操作系統(tǒng)無(wú)關(guān)的功能,其中的部分函數(shù)有一定的計(jì)算量,,是優(yōu)化工作關(guān)注的部分,。
Glibc庫(kù)的版本在不斷更新,本文以當(dāng)前最新的Glibc2.11版本為基礎(chǔ)進(jìn)行優(yōu)化,。
2 龍芯2F體系結(jié)構(gòu)
龍芯2F處理器實(shí)現(xiàn)了64位的MIPSⅢ指令集,,整數(shù)寄存器和浮點(diǎn)寄存器均為64位,支持o32/n32和n64的ABI類(lèi)型,。除了 MIPS標(biāo)準(zhǔn)指令外,,龍芯2F還提供了特有的整型計(jì)算和浮點(diǎn)計(jì)算指令。整型指令包括單條指令對(duì)3個(gè)寄存器進(jìn)行操作的乘法,、除法以及求模運(yùn)算,,浮點(diǎn)指令包括
乘加、開(kāi)平方等運(yùn)算,。
龍芯2F包含兩級(jí)Cache結(jié)構(gòu),,L1 Cache數(shù)據(jù)和指令獨(dú)立,均為64kB;L2 Cache數(shù)據(jù)和指令共享,,為512kB,。L1 cache和L2 cache都采用四路組相聯(lián)結(jié)構(gòu),組內(nèi)采用隨機(jī)替換策略,,cache行大小均為32字節(jié),。
3 Glibc庫(kù)的優(yōu)化
根據(jù)對(duì)Glibc庫(kù)組成的分析,文章對(duì)其中的字符串與內(nèi)存處理,,數(shù)據(jù)轉(zhuǎn)換,,哈希表查找以及加密函數(shù)進(jìn)行了優(yōu)化,以下各節(jié)分別介紹其優(yōu)化方法和優(yōu)化效果,。
3.1 字符串與內(nèi)存處理函數(shù)
字符串與內(nèi)存處理函數(shù)組提供了較為豐富的函數(shù)來(lái)完成各種操作,,包括字符串與內(nèi)存的移動(dòng)、比較和查找等,。兩者的操作通常一一對(duì)應(yīng),,因?yàn)镃語(yǔ)言中的字符串即用一段連續(xù)的內(nèi)存來(lái)表示,區(qū)別在于字符串用空字符NULL來(lái)表示結(jié)尾,,而內(nèi)存塊的結(jié)尾由其大小確定,。
對(duì)本組函數(shù)進(jìn)行分析可知,部分函數(shù)之間的實(shí)現(xiàn)流程類(lèi)似,,如strlen和strnlen,,memcpy和memccpy等。此外某些函數(shù)可通過(guò)調(diào)用其它函數(shù)來(lái)完成,,如strcpy可由strlen與memcpy函數(shù)完成,。
由于這一特點(diǎn),字符串與內(nèi)存處理函數(shù)組的優(yōu)化方法可以相互借鑒,,使用的優(yōu)化方法主要包括以下幾種:
優(yōu)化訪(fǎng)存指令,。本組函數(shù)的處理流程一般是從一個(gè)或兩個(gè)內(nèi)存地址開(kāi)始讀取內(nèi)存并進(jìn)行相應(yīng)的操作。在讀取過(guò)程中考慮內(nèi)存地址的對(duì)齊情況及讀取單位,,分別使用不同格式的訪(fǎng)存指令,。
分塊處理。根據(jù)龍芯2F處理器的寄存器個(gè)數(shù)將待處理的字符串或內(nèi)存分塊,,以充分利用寄存器進(jìn)行循環(huán)展開(kāi),。
匯編實(shí)現(xiàn)。本組函數(shù)包含了一些使用頻繁且對(duì)系統(tǒng)性能影響較大的函數(shù),,如內(nèi)存拷貝memcpy函數(shù)等,對(duì)此類(lèi)函數(shù)我們使用匯編或內(nèi)嵌匯編來(lái)實(shí)現(xiàn),,以避免編譯器可能生成低效的代碼,。
表l是本組函數(shù)中主要函數(shù)的優(yōu)化效果,以字符串規(guī)模或內(nèi)存大小512B作為輸入,。
3.2 數(shù)據(jù)轉(zhuǎn)換函數(shù)
數(shù)據(jù)轉(zhuǎn)換函數(shù)包括字符串轉(zhuǎn)換為整數(shù)和浮點(diǎn)數(shù),。文章分別在每類(lèi)函數(shù)中取一個(gè)例子說(shuō)明優(yōu)化過(guò)程。
字符串轉(zhuǎn)換為整數(shù)包括strtol,、strtoul,、strtoll等函數(shù),它們分別解析不同的整數(shù)類(lèi)型,,支持從2到36的轉(zhuǎn)換進(jìn)制,。各函數(shù)實(shí)現(xiàn)上不同的地方僅在于不同類(lèi)型的整數(shù)大小范圍不同,處理流程類(lèi)似,。下面以strtol函數(shù)為例介紹優(yōu)化過(guò)程,,它的功能為將字符串轉(zhuǎn)換為long型整數(shù)。
Glibc庫(kù)中strtol的實(shí)現(xiàn)使用普通的逐字節(jié)讀取并計(jì)算的方式,。我們首先對(duì)轉(zhuǎn)換進(jìn)制分情況處理,,對(duì)于2的冪次方的進(jìn)制,如2,、4,、8、16,、32,,字符串中的每個(gè)數(shù)字在二進(jìn)制位上沒(méi)有關(guān)聯(lián)??梢詫⑺鼈冎饌€(gè)轉(zhuǎn)換成二進(jìn)制位后填入返回值的相應(yīng)位置,,具有較快的轉(zhuǎn)換速度。其次十進(jìn)制轉(zhuǎn)換是一種常用的情況,,也將其單獨(dú)列出,,可以省去對(duì)字母進(jìn)行判斷。
給定進(jìn)制后,,在該進(jìn)制下整數(shù)至多有多少位就可以確定,。當(dāng)字符串中的合法數(shù)字個(gè)數(shù)超過(guò)位數(shù)限制時(shí),直接返回該類(lèi)型下的最大值即可,。當(dāng)字符串中的合法數(shù)字小于位數(shù)限制時(shí),,可知解析后的結(jié)果絕對(duì)不會(huì)超過(guò)該整數(shù)類(lèi)型的表示范圍,此時(shí)我們將字符串進(jìn)行分段并對(duì)解析進(jìn)程進(jìn)行循環(huán)展開(kāi),。如果合法數(shù)字個(gè)數(shù)恰好等于位數(shù)限制,,此時(shí)解析結(jié)果有超過(guò)該類(lèi)型下最大值的可能性,首先將小于位數(shù)限制的部分解析完成后,,再考慮最后一位數(shù)字,。提前確定解析結(jié)果的范圍可以避免每次循環(huán)內(nèi)都要對(duì)是否超出該類(lèi)型的最大值進(jìn)行判斷,。
取進(jìn)制從2到36,字符串的長(zhǎng)度從1到該進(jìn)制下的最大值進(jìn)行測(cè)試,,得到各進(jìn)制下的優(yōu)化效果如圖1所示,,各進(jìn)制的平均優(yōu)化比率為30.9%。
strtod,、strtof,、strtold等函數(shù)將字符串轉(zhuǎn)換為浮點(diǎn)數(shù)。我們以strtod函數(shù)為例進(jìn)行介紹,,它將字符串轉(zhuǎn)換為double型浮點(diǎn)數(shù),。
Glibc庫(kù)中strtod的實(shí)現(xiàn)使用高精度計(jì)算。首先遍歷整個(gè)字符串,,找出其中的整數(shù),、小數(shù)和指數(shù)部分,各個(gè)部分分別使用高精度計(jì)算解析,,再將結(jié)果合并,。對(duì)于一般的實(shí)現(xiàn)來(lái)說(shuō),各個(gè)部分的取值不會(huì)太大,,此時(shí)使用高精度計(jì)算時(shí)間消耗較大,,改進(jìn)的實(shí)現(xiàn)將每個(gè)部分再進(jìn)行分
塊,對(duì)每個(gè)分塊使用整數(shù)進(jìn)行解析,,實(shí)現(xiàn)方式與strtol相同,。各個(gè)部分的分塊解析完成后,使用一個(gè)long double類(lèi)型作為臨時(shí)變量合并解析結(jié)果以避免精度丟失,,最后將該變量轉(zhuǎn)換為doulble類(lèi)型返回,。對(duì)于strtof函數(shù),使用double類(lèi)型作為臨時(shí)變量,。而對(duì)于strtold函數(shù),,使用上述方法無(wú)法保證精度,仍采用原始的實(shí)現(xiàn),。
由于雙精度浮點(diǎn)數(shù)的有效位數(shù)為16至17位,,對(duì)字符串長(zhǎng)度從1到17進(jìn)行測(cè)試,得到各長(zhǎng)度下的優(yōu)化效果如圖2所示,,各長(zhǎng)度的平均優(yōu)化比率為49.8%,。
3.3 哈希表查找函數(shù)
Glibc庫(kù)中哈希表所包含的關(guān)鍵字和數(shù)據(jù)分別為字符串和內(nèi)存塊,其相關(guān)的函數(shù)包括hcreate,,hdestory以及hsearch,,分別完成哈希表的創(chuàng)建,銷(xiāo)毀和查找,。創(chuàng)建與銷(xiāo)毀操作都是一次性的,,我們對(duì)查找操作進(jìn)行優(yōu)化,。
hsearch函數(shù)讀入字符串關(guān)鍵字作為參數(shù),首先將其映射為整數(shù)關(guān)鍵值,,接著使用雙重散列逐個(gè)取出元素進(jìn)行判斷。
Glibc庫(kù)中字符串映射為整數(shù)的實(shí)現(xiàn)方法為,,首先求得字符串的長(zhǎng)度作為初值,,接著將其不斷左移4位并從末尾到頭部逐個(gè)與字符串中的字符相加。該方法需要對(duì)字符串進(jìn)行兩次遍歷,,并且當(dāng)字符串較長(zhǎng)時(shí),,字符串的長(zhǎng)度和進(jìn)行累加的前幾個(gè)字符會(huì)被移出而不影響最終的映射值。例如對(duì)32位的整型數(shù)來(lái)說(shuō),,只有字符串的前8個(gè)字符對(duì)映射值有影響,。
我們使用ELF哈希算法來(lái)替換原有的映射實(shí)現(xiàn),此算法不先對(duì)字符串求長(zhǎng),,僅進(jìn)行移位和累加操作的循環(huán),,為了避免原始實(shí)現(xiàn)的缺點(diǎn),每次循環(huán)中都會(huì)判斷移位是否超出范圍,,如果超出,,則把中間結(jié)果的高八位異或到低八位上。該哈希函數(shù)只需對(duì)字符串遍歷一遍,,并且考慮了移位越界,,避免了只有前幾個(gè)字符影響映射值的缺陷。
3.4 加密函數(shù)
Glibc庫(kù)中的加密函數(shù)為crypt函數(shù),,該函數(shù)單向加密給定的字符串,,支持的算法包括MD5、SHA以及DES算法,。由于MD5與DES算法的實(shí)現(xiàn)流程固定且做了較充分的展開(kāi),,因此我們主要考慮SHA算法。針對(duì)該算法有設(shè)計(jì)硬件結(jié)構(gòu)進(jìn)行的優(yōu)化,,而我們的工作從代碼實(shí)現(xiàn)角度進(jìn)行,。下面以SHA-256為例說(shuō)明優(yōu)化過(guò)程,其它SHA算法與之類(lèi)似,。
由于龍芯2F只支持小尾端的字符序,,因此SHA算法首先將進(jìn)行運(yùn)算的字轉(zhuǎn)換為大尾端。原始實(shí)現(xiàn)中使用表達(dá)式x=((n<<24)| ((n&0xff00)<<8)|((n>>8)&0xff00)|(n>>24))進(jìn)行轉(zhuǎn)換,,其中n為無(wú)符號(hào)的 32位整數(shù),。該轉(zhuǎn)換操作總共需要兩次與,四次移位與三次或運(yùn)算,,我們對(duì)其
進(jìn)行改進(jìn),,采用二分方的思想,,使用表達(dá)式n1=(n<<16)|(n>>16)和x=(((n1&0xff00ff00)& gt;>8)|(n1&0xff00ff)<<8))。其中計(jì)算n1的表達(dá)式可以由編譯器編譯為一條循環(huán)移位指令,,這樣改進(jìn)的實(shí)現(xiàn)共需要兩次與,,三次移位與一次或運(yùn)算,省去了一次移位與兩次或運(yùn)算,。
算法接下來(lái)的操作是消息擴(kuò)散與迭代計(jì)算,,計(jì)算公式分別如圖3和圖4所示。
我們對(duì)其計(jì)算過(guò)程進(jìn)行層數(shù)為2的循環(huán)展開(kāi),。對(duì)于迭代計(jì)算過(guò)程,,循環(huán)展開(kāi)之后還可以繼續(xù)進(jìn)行賦值傳遞優(yōu)化,減少運(yùn)算量和迭代次數(shù),。循環(huán)展開(kāi)層數(shù)增加時(shí),,循環(huán)次數(shù)減小,但增加了循環(huán)內(nèi)的計(jì)算量,,寄存器的數(shù)目滿(mǎn)足不了中間結(jié)果的保存,,需要讀寫(xiě)內(nèi)存,反而造成性能的下降,。經(jīng)過(guò)實(shí)驗(yàn)確定,,層數(shù)為2是一個(gè)較好的選擇。
表2是改進(jìn)前后SHA256算法的性能對(duì)比,,取數(shù)據(jù)大小從64B到2kB倍增,。
4 總結(jié)
Glibc庫(kù)是Linux系統(tǒng)最底層的運(yùn)行庫(kù),是所有應(yīng)用程序賴(lài)以執(zhí)行的基礎(chǔ)環(huán)境,。本文基于龍芯2F平臺(tái)對(duì)Glibc庫(kù)中的字符串與內(nèi)存處理函數(shù),、數(shù)據(jù)轉(zhuǎn)換函數(shù)、哈希表查找函數(shù)以及加密函數(shù)進(jìn)行了代碼優(yōu)化,,大部分函數(shù)的優(yōu)化比率達(dá)到30%以上,,對(duì)龍芯2F平臺(tái)的整體運(yùn)行性能提升具有重要意義。