4.3.2 內(nèi)存拷貝
正如第3節(jié)所解釋的那樣,數(shù)據(jù)包在到達(dá)用戶程序前要經(jīng)歷兩次器拷貝(圖1):第一次,先從NIC緩沖區(qū)轉(zhuǎn)移到核心緩存區(qū)(圖2)。第二次則將它傳輸?shù)接脩魬?yīng)用程序緩存。圖5展示了在測(cè)試平臺(tái)的電腦上兩次數(shù)據(jù)拷貝的時(shí)鐘開銷,及對(duì)應(yīng)的數(shù)據(jù)包的大小。
根據(jù)NDIS規(guī)范,由函數(shù)NdisTransferData()實(shí)施并完成第一次傳輸。該函數(shù)的開銷特別高,主要有兩個(gè)原因。
1 .在拷貝過程前需要有一些附加的開銷??紤]到NIC驅(qū)動(dòng)程序在退出包驅(qū)動(dòng)器控制權(quán)以后,整個(gè)數(shù)據(jù)包可能失效,故根據(jù)DDK文檔[7],驅(qū)動(dòng)器必須使用該功能函數(shù)。這樣,該功能函數(shù)首先檢查整個(gè)數(shù)據(jù)包是否已被NIC傳輸?shù)絻?nèi)存;如果還沒有,則等待直到傳輸完成。
2 .該功能函數(shù)所操作的數(shù)據(jù)對(duì)象并不在CPU緩存中。數(shù)據(jù)包通過控制總線從NIC板載存儲(chǔ)器傳輸?shù)街鞔?參見3.1)。
先前兩點(diǎn)解釋了圖5所示的第一次拷貝高開銷的原因。在處理過程中,存在有一個(gè)開銷量底限基數(shù)的客觀事實(shí),它獨(dú)立于處理數(shù)據(jù)量的大小(這就是為什么在數(shù)據(jù)包較小的情況下,處理每字節(jié)需要更高的代價(jià))。而當(dāng)數(shù)據(jù)包較大時(shí),這些開銷平攤于每個(gè)字節(jié),故每字節(jié)開銷量要小一些。
第二次拷貝使用的是標(biāo)準(zhǔn)C庫(kù)函數(shù)(例如memcpy())。由于待拷貝到用戶區(qū)的數(shù)據(jù)可能大都不在于CPU的cache中,故處理每字節(jié)的平均代價(jià)隨著數(shù)據(jù)包的增大而增大,原因就是命中率下降了。另外處理每字節(jié)數(shù)據(jù)的開銷量還與核心層存儲(chǔ)區(qū)的大小有關(guān)。例如,如果核心存儲(chǔ)區(qū)容量較小,那么從NIC存儲(chǔ)區(qū)拷貝數(shù)據(jù)時(shí),會(huì)出現(xiàn)有部分?jǐn)?shù)據(jù)仍保留在CPU cache中。
總結(jié),第一次拷貝每個(gè)包的時(shí)鐘周期開銷在540到10500之間,第二次拷貝在259到8550之間變化。實(shí)際上,考慮到每個(gè)包在存入核心存儲(chǔ)區(qū)前都要插入一個(gè)含有20個(gè)字節(jié)的幀頭(包含時(shí)間戳、包長(zhǎng)度等其它信息),所以第二次拷貝的總開銷在364到8664之間。
4.3.3 應(yīng)用程序
所有應(yīng)用程序與包驅(qū)動(dòng)程序之間的交互作用都是通過系統(tǒng)調(diào)用來完成。Windows提供了 ReadFile()、WriteFile(),還有DeviceIO-Control()等系統(tǒng)調(diào)用來完成I/O操作。所有這些系統(tǒng)調(diào)用都要完成兩部分內(nèi)容"context switches"(在這里使用context switch這個(gè)詞其實(shí)并不太準(zhǔn)確。實(shí)際上從用戶層到核心層這一過程是個(gè)優(yōu)先特權(quán)轉(zhuǎn)閘,而非上下文執(zhí)行轉(zhuǎn)閘):第一,將指令從用戶層(應(yīng)用程序)發(fā)送到核心層(驅(qū)動(dòng)程序)。第二,返回用戶應(yīng)用程序的控制權(quán)。
眾所周知,上下文轉(zhuǎn)閘是相當(dāng)復(fù)雜(它通常包含中斷的產(chǎn)生和一些數(shù)據(jù)結(jié)構(gòu)的初始化工作)并且開銷很大的過程。如在我們的測(cè)試平臺(tái)上,類似read()這樣的系統(tǒng)調(diào)用需要33500個(gè)時(shí)鐘周期。如此高的開銷!若每次系統(tǒng)調(diào)用只拷貝單個(gè)數(shù)據(jù)包,那可想效率多低了。因此包捕獲器在每次應(yīng)用程序系統(tǒng)調(diào)用時(shí)傳輸整塊的數(shù)據(jù)包。每次系統(tǒng)調(diào)用所傳輸?shù)臄?shù)據(jù)包數(shù)量的多少取決于核心層存儲(chǔ)區(qū)的大小,并與CPU負(fù)載能力成正比。另外,它還取決于用戶層應(yīng)用程序的復(fù)雜性(如果應(yīng)用程序?qū)?shù)據(jù)包的處理時(shí)間很長(zhǎng),那它從核心存儲(chǔ)區(qū)重載數(shù)據(jù)的時(shí)間間隔就大些,那么核心緩沖區(qū)的數(shù)據(jù)就沒能及時(shí)“排干”),而另一方面還受包捕獲驅(qū)動(dòng)器的影響(驅(qū)動(dòng)器內(nèi)的代碼擁有較高的執(zhí)行優(yōu)先級(jí),因此核心緩沖區(qū)常處于滿載狀態(tài))。
由于讀操作的頻率,以及每次系統(tǒng)調(diào)用所處理的數(shù)據(jù)包的數(shù)量都是隨機(jī)數(shù)(變化無常),故無法對(duì)每個(gè)包處理所需的時(shí)鐘開銷作出明確描述。在一臺(tái)過載的機(jī)器上(即CPU的使用率達(dá)到100%),假定它接收到的是最小幀,同時(shí)捕獲驅(qū)動(dòng)器在每次系統(tǒng)調(diào)用時(shí)傳輸256k個(gè)字節(jié)(該值為目前WinPcap上可選擇的界值。),這相當(dāng)于3200個(gè)數(shù)據(jù)包(包括由驅(qū)動(dòng)程序后來附增加的20個(gè)字節(jié)的幀頭)。在這種情況下,處理每個(gè)包的上下文轉(zhuǎn)閘(context switch)近似代價(jià)為10個(gè)時(shí)鐘周期,但這相比于我們接下來要講的其它組件的開銷,這甚至可以被忽略。
4.3.4 其它處理部件
盡管我們一般都下意識(shí)地認(rèn)為:在數(shù)據(jù)包過濾和傳輸拷貝過程中,捕獲驅(qū)動(dòng)器需耗費(fèi)的執(zhí)行時(shí)間是最多的(這也正是為什么幾乎所有文獻(xiàn)都將注意力集中于它以達(dá)到增進(jìn)性能的效果)。但通過我們的測(cè)試,揭示了其它一些關(guān)系到包捕獲代價(jià)的關(guān)鍵因素。在它們中間,又以timestamp最為顯著。
NPF驅(qū)動(dòng)程序通過KeQueryPerformanceCounter()win32功能函數(shù)獲得數(shù)據(jù)包的時(shí)間戳,它是唯一能提供微秒級(jí)精度的核心函數(shù)。該函數(shù)與系統(tǒng)晶振相關(guān)聯(lián),故其開銷十分巨大:在我們的測(cè)試平臺(tái)上,大約需要1800個(gè)時(shí)鐘周期(通過對(duì)若干單機(jī)器進(jìn)行測(cè)試,得到該值)。該函數(shù)要耗費(fèi)若干個(gè)微秒才能返回一個(gè)帶有微秒級(jí)精確定時(shí)的結(jié)果,這不免有些荒謬,但事實(shí)正是如此。更有甚是,它必須持續(xù)運(yùn)作以便在數(shù)據(jù)包到達(dá)時(shí)為之加上準(zhǔn)確的時(shí)間戳。
額外的開銷還包括與NDIS、與核心層的交互運(yùn)作(交互運(yùn)作大都需要使用系統(tǒng)調(diào)用函數(shù),因此開銷巨大)。另外還包括管理(內(nèi)存映射與解映)核心層緩沖區(qū)、在NPF中向數(shù)據(jù)包增創(chuàng)幀??傊ミ^濾和拷貝的開銷,其它還有近830個(gè)時(shí)鐘周期的開銷。
4.4 全部開銷
圖6以園盤切片形式形象展示了各相關(guān)組件的時(shí)鐘開銷。處理一個(gè)包的全部的費(fèi)用是 5680 個(gè)鐘周期。該測(cè)試結(jié)果生成的相關(guān)配置如下所示:流速為148Kfps,數(shù)據(jù)包大小為64個(gè)字節(jié),適配器為3Com 3C996 Gigabit,過濾處理虛指令條數(shù)為21條。
顯然,從圖6可以看出,當(dāng)數(shù)據(jù)包較小時(shí),時(shí)間戳和NIC驅(qū)動(dòng)的開銷最大。又由于這兩部分的開銷大都取決于硬件,故進(jìn)行軟件優(yōu)化意義不大。一些針對(duì)NIC驅(qū)動(dòng)進(jìn)行的小規(guī)模優(yōu)化程序由于其開發(fā)商拒絕公開代碼,使得它們得不到廣泛的應(yīng)用。然而在任何情況下,NIC驅(qū)動(dòng)軟件優(yōu)化的效果遠(yuǎn)不如升級(jí)NIC卡上的芯片組。
最值得注意的是,圖6說明了為什么大多數(shù)文獻(xiàn)都將優(yōu)化集中于拷貝和過濾這兩個(gè)過程,以減少系統(tǒng)開銷。但事實(shí)上,其結(jié)果卻相當(dāng)有限:僅可能帶來性能15%的提升。
在我們所做的性能分析中,大都使用體積較小的數(shù)據(jù)包。需要解釋的是,這并不是因?yàn)槭艿侥欠矫娴南拗?。舉例來說,眾多網(wǎng)絡(luò)分析工具(尤其像sniffers和network monitors)都只提取包的起始部分,例如前98個(gè)字節(jié),剩下的部分將被丟棄。這正是我們?cè)跍y(cè)試中考慮使用小數(shù)據(jù)包的原因。
4.5 對(duì)測(cè)試結(jié)果的附加說明
盡管我們的測(cè)試結(jié)果取自特定平臺(tái)上(Win32)的特定工具(WinPcap),但結(jié)果還是具有普遍意義的。WinPcap(也就是tap處理、第一及第二次拷貝、過濾)的開銷類似于其它體系結(jié)構(gòu)上的開銷(例如,NPF與BPF相當(dāng)類似)。同樣的道理,的相關(guān)開銷也都相差不多:NIC驅(qū)動(dòng)、時(shí)間戳timestamp、上下文轉(zhuǎn)閘。NIC驅(qū)動(dòng)的開銷可以通過將一些原先的軟件功能在設(shè)計(jì)網(wǎng)卡時(shí)固化到芯片上的方式來減少,但這卻需花費(fèi)昂貴的成本?;谟布臅r(shí)間戳是最有效的優(yōu)化之一:Endace's DAG卡的廣泛使用[17]就是其中一個(gè)例子?;诘挠布捎谠谠O(shè)計(jì)時(shí)缺少專業(yè)芯片,故沒能提供任何簡(jiǎn)單的方法來獲得精確時(shí)間戳,而只能通過內(nèi)部邏輯(例如,借助于CPU計(jì)數(shù)器)。更糟的是,精確定時(shí)是從8253/8254芯片(或其它類似芯片)中獲取,然而因?yàn)樗鼈冃柰ㄟ^系統(tǒng)總線IN/OUT操作,故其存取是相當(dāng)慢的。
最后再提一點(diǎn),可以忽略上下文轉(zhuǎn)閘的影響,因?yàn)樵诓煌牟僮飨到y(tǒng)中其情況大都相差無幾(例如,在調(diào)制解調(diào)器操作系統(tǒng)中,它被作為一項(xiàng)需謹(jǐn)慎設(shè)置的參數(shù))。
5 .最優(yōu)
針對(duì)先前幾節(jié)內(nèi)容講述的這些突出瓶頸,本節(jié)將介紹并評(píng)估在NPF中所做的優(yōu)化處理。
5.1 過濾部件
WinPcap使用的過濾系統(tǒng)是BSD包過濾器(BPF),1993[2]它被提議使用。當(dāng)時(shí)文獻(xiàn)記載的還有其它一些過濾系統(tǒng)[9][10][11][12],但是在普通操作環(huán)境下,它們的性能根本無法與BPF相比。
在對(duì)BPF的優(yōu)化解決方案中,動(dòng)態(tài)代碼的產(chǎn)生(即,將包過濾代碼編譯為CPU執(zhí)行指令)確保了出色的性能改進(jìn)[11][10]。因此,Just In Time(JIT)engine引擎被整合入NPF,通過它將BPF過濾代碼編譯成80x86的二進(jìn)制代碼。圖7所示,該優(yōu)化措施帶來了3.1到5的提升。這相當(dāng)實(shí)現(xiàn)了對(duì)捕獲機(jī)制總體性能8%的提升(假設(shè)為一個(gè)虛指令條數(shù)規(guī)模為21的過濾器)。
5.2 存儲(chǔ)器拷貝
先前已經(jīng)說了,第一次數(shù)據(jù)包拷貝(從NIC存儲(chǔ)器到核心緩沖區(qū))的開銷比第2次的開銷來得大。原因是由于它有個(gè)額外處理NdisTransferData()。然而,我們注意到幾乎所有網(wǎng)絡(luò)控制器(絕大多數(shù)網(wǎng)絡(luò)適配器)在通報(bào)NIC驅(qū)動(dòng)程序前已將一個(gè)數(shù)據(jù)包完整地傳輸?shù)酱鎯?chǔ)器中,因此NPF驅(qū)動(dòng)程序可在一個(gè)鄰接的緩沖區(qū)中得到它。既然這樣,我們放棄舊的方法,而是通過一個(gè)標(biāo)準(zhǔn)C庫(kù)函數(shù)來完成拷貝工作,結(jié)果如圖8顯示。由于數(shù)據(jù)包成塊地傳輸,同時(shí)在CPU緩存中保持較高的命中率,這樣第2次拷貝的速度也有些許提高。這回兩次拷貝的開銷相差不多且具有相同的變化趨勢(shì)。
感謝這一優(yōu)化措施。在數(shù)據(jù)包大小為64byte的滿載機(jī)器上,第1次拷貝過程的開銷從540個(gè)時(shí)鐘周期降到了300,當(dāng)然第二次拷貝過程的開銷變化不大。這樣,對(duì)于整個(gè)捕獲機(jī)制帶來了4%的性能提升。
5.3 時(shí)間戳
可以通過目前32位Intel處理器中的timestamp Counter(TSC)計(jì)數(shù)器來取代KeQueryPerformanceCounter(),并由它來得到微秒級(jí)精確時(shí)間戳。該高效計(jì)數(shù)器在每個(gè)處理器時(shí)鐘周期都完成一次自增,因此它的精密近乎CPU時(shí)鐘頻率。而且x86匯編器提供一個(gè)快速(僅一個(gè)周期)指令-rdtsc來得到精確時(shí)間戳。