加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 1、為什么需要DPDK?
    • 2、鳥瞰DPDK
    • 3、電信云中數(shù)據(jù)包轉(zhuǎn)發(fā)性能提升中的DPDK
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

DPDK技術(shù)棧在電信云中的最佳實(shí)踐(1)

2021/04/20
407
閱讀需 32 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

DPDK是一個(gè)技術(shù)棧,主要用于Intel架構(gòu)的服務(wù)器領(lǐng)域,其主要目的就是提升x86標(biāo)準(zhǔn)服務(wù)器的轉(zhuǎn)發(fā)性能。因此,本文只重點(diǎn)介紹DPDK平臺(tái)部分技術(shù)在電信云中的最佳實(shí)踐。

1、為什么需要DPDK?

在IA上,網(wǎng)絡(luò)數(shù)據(jù)包處理遠(yuǎn)早于DPDK而存在。從商業(yè)版的Windows到開源Linux操作系統(tǒng),所有跨主機(jī)通信幾乎都會(huì)涉及網(wǎng)絡(luò)協(xié)議棧以及底層網(wǎng)卡驅(qū)動(dòng)對(duì)于數(shù)據(jù)包的處理。然而,低速網(wǎng)絡(luò)數(shù)據(jù)轉(zhuǎn)發(fā)與高速網(wǎng)絡(luò)數(shù)據(jù)轉(zhuǎn)發(fā)的處理對(duì)系統(tǒng)的要求完全不一樣。以Linux為例,傳統(tǒng)網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)包處理的動(dòng)作可以概括如下:

數(shù)據(jù)包到達(dá)網(wǎng)卡設(shè)備。

網(wǎng)卡設(shè)備依據(jù)配置進(jìn)行DMA操作。 

網(wǎng)卡發(fā)送中斷,喚醒處理器

驅(qū)動(dòng)軟件填充讀寫緩沖區(qū)數(shù)據(jù)結(jié)構(gòu)。

數(shù)據(jù)報(bào)文達(dá)到內(nèi)核協(xié)議棧,進(jìn)行高層處理。

如果最終應(yīng)用在用戶態(tài),數(shù)據(jù)從內(nèi)核搬移到用戶態(tài)。

如果最終應(yīng)用在內(nèi)核態(tài),在內(nèi)核繼續(xù)進(jìn)行。

隨著網(wǎng)絡(luò)接口帶寬從千兆向萬兆邁進(jìn),原先每個(gè)報(bào)文就會(huì)觸發(fā)一個(gè)中斷,中斷帶來的開銷變得突出,大量數(shù)據(jù)到來會(huì)觸發(fā)頻繁的中斷開銷,導(dǎo)致系統(tǒng)無法承受。

在網(wǎng)絡(luò)包高性能轉(zhuǎn)發(fā)技術(shù)領(lǐng)域,有兩個(gè)著名的技術(shù)框架NAPI和Netmap。NAPI策略用于高吞吐的場(chǎng)景,其策略是系統(tǒng)被中斷喚醒后,盡量使用輪詢的方式一次處理多個(gè)數(shù)據(jù)包,直到網(wǎng)絡(luò)再次空閑重新轉(zhuǎn)入中斷等待,其目的就是解決數(shù)據(jù)包在轉(zhuǎn)發(fā)過程過程中頻繁中斷引入的大量系統(tǒng)開銷。Netmap就是采用共享數(shù)據(jù)包池的方式,減少內(nèi)核到用戶空間的包復(fù)制,從而解決大多數(shù)場(chǎng)景下需要把包從內(nèi)核的緩沖區(qū)復(fù)制到用戶緩沖區(qū)引入大量系統(tǒng)開銷問題。

NAPI與Netmap兩方面的努力其實(shí)已經(jīng)明顯改善了傳統(tǒng)Linux系統(tǒng)上的包處理能力,但是,Linux作為分時(shí)操作系統(tǒng),要將CPU的執(zhí)行時(shí)間合理地調(diào)度給需要運(yùn)行的任務(wù)。相對(duì)于公平分時(shí),不可避免的就是適時(shí)調(diào)度。早些年CPU核數(shù)比較少,為了每個(gè)任務(wù)都得到響應(yīng)處理,進(jìn)行充分分時(shí),用效率換響應(yīng),是一個(gè)理想的策略?,F(xiàn)今CPU核數(shù)越來越多,性能越來越強(qiáng),為了追求極端的高性能高效率,分時(shí)就不一定總是上佳的策略。以Netmap為例,即便其減少了內(nèi)核到用戶空間的內(nèi)存復(fù)制,但內(nèi)核驅(qū)動(dòng)的收發(fā)包處理和用戶態(tài)線程依舊由操作系統(tǒng)調(diào)度執(zhí)行,除去任務(wù)切換本身的開銷,由切換導(dǎo)致的后續(xù)cache替換(不同任務(wù)內(nèi)存熱點(diǎn)不同),對(duì)性能也會(huì)產(chǎn)生負(fù)面的影響。為此,Intel針對(duì)IA架構(gòu)的這些問題,就提出了DPDK技術(shù)棧的架構(gòu),其根本目的就是盡量采用用戶態(tài)驅(qū)動(dòng)能力來替代內(nèi)核態(tài)驅(qū)動(dòng),從而減少內(nèi)核態(tài)的開銷,提升轉(zhuǎn)發(fā)性能。

2、鳥瞰DPDK

什么是DPDK?在《DPDK深入淺出》一書中,有以下一段描述:

針對(duì)不同的對(duì)象,其定義并不相同。對(duì)于普通用戶來說,它可能是一個(gè)性能出色的包數(shù)據(jù)處理加速軟件庫;對(duì)于開發(fā)者來說,它可能是一個(gè)實(shí)踐包處理新想法的創(chuàng)新工場(chǎng);對(duì)于性能調(diào)優(yōu)者來說,它可能又是一個(gè)絕佳的成果分享平臺(tái)。當(dāng)下火熱的網(wǎng)絡(luò)功能虛擬化,則將DPDK放在一個(gè)重要的基石位置。

DPDK最初的動(dòng)機(jī)很簡單,就是為了證明IA多核處理器能夠支撐高性能數(shù)據(jù)包處理。隨著早期目標(biāo)的達(dá)成和更多通用處理器體系的加入,DPDK逐漸成為通用多核處理器高性能數(shù)據(jù)包處理的業(yè)界標(biāo)桿。

目前,DPDK技術(shù)主要應(yīng)用于計(jì)算領(lǐng)域的硬件加速器、通信領(lǐng)域的網(wǎng)絡(luò)處理器和IT領(lǐng)域的多核處理器。隨著軟件(例如,DPDK)在I/O性能提升上的不斷創(chuàng)新,將多核處理器的競(jìng)爭(zhēng)力提升到一個(gè)前所未有的高度。在SDN/NFV領(lǐng)域,DPDK技術(shù)得到了空前應(yīng)用,產(chǎn)生了不少最佳實(shí)踐案例。

DPDK提出的目的就是為IA上的高速包處理。下圖所示的DPDK主要模塊分解展示了以基礎(chǔ)軟件庫的形式,為上層應(yīng)用的開發(fā)提供一個(gè)高性能的基礎(chǔ)I/O開發(fā)包。主要利用了有助于包處理的軟硬件特性,如大頁、緩存行對(duì)齊、線程綁定、預(yù)取、NUMA、IA最新指令的利用、Intel DDIO、內(nèi)存交叉訪問等。

核心庫Core Libs,提供系統(tǒng)抽象、大頁內(nèi)存、緩存池、定時(shí)器及無鎖環(huán)等基礎(chǔ)組件。

PMD庫,提供全用戶態(tài)的驅(qū)動(dòng),以便通過輪詢和線程綁定得到極高的網(wǎng)絡(luò)吞吐,支持各種本地和虛擬的網(wǎng)卡。

Classify庫,支持精確匹配(Exact Match)、最長匹配(LPM)和通配符匹配(ACL),提供常用包處理的查表操作。

QoS庫,提供網(wǎng)絡(luò)服務(wù)質(zhì)量相關(guān)組件,如限速(Meter)和調(diào)度(Sched)。

除了這些組件,DPDK 還提供了幾個(gè)平臺(tái)特性,比如節(jié)能考慮的運(yùn)行時(shí)頻率調(diào)整(POWER),與Linux kernel stack建立快速通道的 KNI(Kernel Network Interface)。而Packet Framework和DISTRIB為搭建更復(fù)雜的多核流水線處理模型提供了基礎(chǔ)的組件。

DPDK軟件包內(nèi)有一個(gè)最基本的三層轉(zhuǎn)發(fā)實(shí)例(l3fwd),可用于測(cè)試雙路服務(wù)器整系統(tǒng)的吞吐能力,通過現(xiàn)場(chǎng)實(shí)驗(yàn),可以達(dá)到220Gbit/s的數(shù)據(jù)報(bào)文吞吐能力。除了通過硬件或者軟件提升性能之外,如今DPDK整系統(tǒng)報(bào)文吞吐能力上限已經(jīng)不再受限于CPU的核數(shù),當(dāng)前瓶頸在于PCIe(IO總線)的LANE數(shù)。換句話說,系統(tǒng)性能的整體I/O天花板不再是CPU,而是系統(tǒng)所提供的所有PCIe LANE的帶寬,也就是能插入多少個(gè)高速以太網(wǎng)接口卡。

在這樣的性能基礎(chǔ)上,網(wǎng)絡(luò)節(jié)點(diǎn)的軟化(NFV)就成為可能。對(duì)于網(wǎng)絡(luò)節(jié)點(diǎn)上運(yùn)轉(zhuǎn)的不同形態(tài)的網(wǎng)絡(luò)功能,通過軟化并適配到一個(gè)通用的硬件平臺(tái),就是軟硬件解耦。解耦正是NFV的一個(gè)核心思想,而硬件解耦的多個(gè)網(wǎng)絡(luò)功能在單一通用節(jié)點(diǎn)上的隔離共生問題,就是另一個(gè)核心思想---虛擬化。

3、電信云中數(shù)據(jù)包轉(zhuǎn)發(fā)性能提升中的DPDK

cache的作用

在當(dāng)今服務(wù)器領(lǐng)域,一個(gè)處理器通常包含多個(gè)核心(Core),集成Cache子系統(tǒng),內(nèi)存子系統(tǒng)通過內(nèi)部或外部總線與其通信。在經(jīng)典計(jì)算機(jī)系統(tǒng)中一般都有兩個(gè)標(biāo)準(zhǔn)化的部分:北橋(North Bridge)和南橋(SouthBridge)。它們是處理器和內(nèi)存以及其他外設(shè)溝通的渠道。在這類系統(tǒng)中,北橋就是真?zhèn)€架構(gòu)的瓶頸,一旦北橋處理不過來或故障,整個(gè)系統(tǒng)的處理效率就會(huì)變低或癱瘓。因此,后來計(jì)算機(jī)系統(tǒng)中只存在南橋芯片,而北橋部分就被全部移植到CPU的SoC中,其中最重要的部分就是內(nèi)存控制器,并在此基礎(chǔ)上進(jìn)一步衍生出NUMA和MPP架構(gòu),這個(gè)放在后面會(huì)講。

我們?cè)诒究茖W(xué)習(xí)計(jì)算機(jī)基礎(chǔ)課程時(shí),都知道計(jì)算機(jī)的內(nèi)存分為SRAM、DRAM、SDRAMDDR(1/2/3/4)等不同類型。在早期的PC系統(tǒng)中,主要使用DRAM和SDRAM來作為內(nèi)存,相比SRAM在成本、功耗方面有不小的優(yōu)勢(shì),而且速度也還可以。后來在現(xiàn)今的PC系統(tǒng)中,利用SDRAM在一個(gè)時(shí)鐘周期的上下邊沿進(jìn)行數(shù)據(jù)讀寫,整體數(shù)據(jù)吞吐率帶寬翻倍,也就是DDR RAM,DDR根據(jù)不同的主頻,又分為DDR1/DDR2/DDR3/DDR4。而SRAM,由于其功耗高、成本高,速度很快,一般都作為CPU的cache使用,目前都被封裝的CPU的SoC中。

一般來說,Cache由三級(jí)組成,之所以對(duì)Cache進(jìn)行分級(jí),也是從成本和生產(chǎn)工藝的角度考慮的。一級(jí)(L1)最快,但是容量最?。蝗?jí)(LLC,Last Level Cache)最慢,但是容量最大。

一級(jí)Cache:一般分為數(shù)據(jù)Cache和指令Cache,數(shù)據(jù)Cache用來存儲(chǔ)數(shù)據(jù),而指令Cache用于存放指令。這種Cache速度最快,一般處理器只需要3~5個(gè)指令周期就能訪問到數(shù)據(jù),因此成本高,容量小,一般都只有幾十KB。

二級(jí)Cache:和一級(jí)Cache分為數(shù)據(jù)Cache和指令Cache不同,數(shù)據(jù)和指令都無差別地存放在一起。速度相比一級(jí)Cache慢一些,處理器大約需要十幾個(gè)處理器周期才能訪問到數(shù)據(jù),容量也相對(duì)來說大一些,一般有幾百KB到幾MB不等。

三級(jí)Cache:速度更慢,處理器需要幾十個(gè)處理器周期才能訪問到數(shù)據(jù),容量更大,一般都有幾MB到幾十個(gè)MB。在多核處理器內(nèi)部,三級(jí)Cache由所有的核心所共有。這樣的共享方式,其實(shí)也帶來一個(gè)問題,有的處理器可能會(huì)極大地占用三級(jí)Cache,導(dǎo)致其他處理器只能占用極小的容量,從而導(dǎo)致Cache不命中,性能下降。因此,Intel公司推出了Intel® CAT技術(shù),確保有一個(gè)公平,或者說軟件可配置的算法來控制每個(gè)核心可以用到的Cache大小。

為了將cache與內(nèi)存進(jìn)行關(guān)聯(lián),需要對(duì)cache和內(nèi)存進(jìn)行分塊,并采用一定的映射算法進(jìn)行關(guān)聯(lián)。分塊就是將Cache和內(nèi)存以塊為單位進(jìn)行數(shù)據(jù)交換,塊的大小通常以在內(nèi)存的一個(gè)存儲(chǔ)周期中能夠訪問到的數(shù)據(jù)長度為限。當(dāng)今主流塊的大小都是64字節(jié),因此一個(gè)Cache line就是指64個(gè)字節(jié)大小的數(shù)據(jù)塊。而映射算法是指把內(nèi)存地址空間映射到Cache地址空間。具體來說,就是把存放在內(nèi)存中的內(nèi)容按照一定規(guī)則裝入到Cache中,并建立內(nèi)存地址與Cache地址之間的對(duì)應(yīng)關(guān)系。當(dāng)CPU需要訪問這個(gè)數(shù)據(jù)塊內(nèi)容時(shí),只需要把內(nèi)存地址轉(zhuǎn)換成Cache地址,從而在Cache中找到該數(shù)據(jù)塊,最終返回給CPU。

根據(jù)Cache和內(nèi)存之間的映射關(guān)系的不同,Cache可以分為三類:第一類是全關(guān)聯(lián)型Cache(full associative cache),第二類是直接關(guān)聯(lián)型Cache(direct mapped cache),第三類是組關(guān)聯(lián)型Cache(N-ways associative cache)。

全關(guān)聯(lián)型cache:需要在cache中建立一個(gè)目錄表,目錄表的每一項(xiàng)由內(nèi)存地址、cache塊號(hào)和一個(gè)有效位組成。當(dāng)CPU需要訪問某個(gè)內(nèi)存地址時(shí),首先查詢?cè)撃夸洷砼袛嘣搩?nèi)容是否緩存在Cache中,如果在,就直接從cache中讀取內(nèi)容;如果不在,就去通過內(nèi)存地址轉(zhuǎn)換去內(nèi)存沖讀取。具體原理如下:

首先,用內(nèi)存的塊地址A在Cache的目錄表中進(jìn)行查詢,如果找到等值的內(nèi)存塊地址,檢查有效位是否有效,只有有效的情況下,才能通過Cache塊號(hào)在Cache中找到緩存的內(nèi)存,并且加上塊內(nèi)地址B,找到相應(yīng)數(shù)據(jù),這時(shí)則稱為Cache命中,處理器拿到數(shù)據(jù)返回;否則稱為不命中,CPU則需要在內(nèi)存中讀取相應(yīng)的數(shù)據(jù)。使用全關(guān)聯(lián)型Cache,塊的沖突最小(沒有沖突),Cache的利用率也高,但是需要一個(gè)訪問速度很快的相聯(lián)存儲(chǔ)器。隨著Cache容量的增加,其電路設(shè)計(jì)變得十分復(fù)雜,因此一般只有TLB cache才會(huì)設(shè)計(jì)成全關(guān)聯(lián)型。

直接關(guān)聯(lián)型Cache:是指將某一塊內(nèi)存映射到Cache的一個(gè)特定的塊,即Cache line中。假設(shè)一個(gè)Cache中總共存在N個(gè)Cache line,那么內(nèi)存就被分成N等分,其中每一等分對(duì)應(yīng)一個(gè)Cache line。比如:Cache的大小是2K,而一個(gè)Cache line的大小是64B,那么就一共有2K/64B=32個(gè)Cache line,那么對(duì)應(yīng)我們的內(nèi)存,第1塊(地址0~63),第33塊(地址64*32~64*33-1),以及第(N*32+1)塊都被映射到Cache第一塊中;同理,第2塊,第34塊,以及第(N*32+2)塊都被映射到Cache第二塊中;可以依次類推其他內(nèi)存塊。直接關(guān)聯(lián)型Cache的目錄表只有兩部分組成:區(qū)號(hào)和有效位。具體原理如下:

首先,內(nèi)存地址被分成三部分:區(qū)號(hào)A、塊號(hào)B和塊內(nèi)地址C。根據(jù)區(qū)號(hào)A在目錄表中找到完全相等的區(qū)號(hào),并且在有效位有效的情況下,說明該數(shù)據(jù)在Cache中,然后通過內(nèi)存地址的塊號(hào)B獲得在Cache中的塊地址,加上塊內(nèi)地址C,最終找到數(shù)據(jù)。如果在目錄表中找不到相等的區(qū)號(hào),或者有效位無效的情況下,則說明該內(nèi)容不在Cache中,需要到內(nèi)存中讀取。可以看出,直接關(guān)聯(lián)是一種很“死”的映射方法,當(dāng)映射到同一個(gè)Cache塊的多個(gè)內(nèi)存塊同時(shí)需要緩存在Cache中時(shí),只有一個(gè)內(nèi)存塊能夠緩存,其他塊需要被“淘汰”掉。因此,直接關(guān)聯(lián)型命中率是最低的,但是其實(shí)現(xiàn)方式最為簡單,匹配速度也最快。

組關(guān)聯(lián)型Cache:是目前Cache中用的比較廣泛的一種方式,是前兩種Cache的折中形式。在這種方式下,內(nèi)存被分為很多組,一個(gè)組的大小為多個(gè)Cache line的大小,一個(gè)組映射到對(duì)應(yīng)的多個(gè)連續(xù)的Cache line,也就是一個(gè)Cache組,并且該組內(nèi)的任意一塊可以映射到對(duì)應(yīng)Cache組的任意一個(gè)??梢钥闯觯诮M外,其采用直接關(guān)聯(lián)型Cache的映射方式,而在組內(nèi),則采用全關(guān)聯(lián)型Cache的映射方式。比如:有一個(gè)4路組關(guān)聯(lián)型Cache,其大小為1M,一個(gè)Cache line的大小為64B,那么總共有16K個(gè)Cache line,但是在4路組關(guān)聯(lián)的情況下,就擁有了4K個(gè)組,每個(gè)組有4個(gè)Cache line。一個(gè)內(nèi)存單元可以緩存到它所對(duì)應(yīng)的組中的任意一個(gè)Cache line中去。具體原理如下:

目錄表由三部分組成:“區(qū)號(hào)+塊號(hào)”、Cache塊號(hào)和有效位。一個(gè)內(nèi)存地址被分成四部分:區(qū)號(hào)A、組號(hào)B、塊號(hào)C和塊內(nèi)地址D。首先,根據(jù)組號(hào)B按地址查找到一組目錄表項(xiàng);然后,根據(jù)區(qū)號(hào)A和塊號(hào)C在該組中進(jìn)行關(guān)聯(lián)查找(即并行查找,為了提高效率),如果匹配且有效位有效,則表明該數(shù)據(jù)塊緩存在Cache中,得到Cache塊號(hào),加上塊內(nèi)地址D,可以得到該內(nèi)存地址在Cache中映射的地址,得到數(shù)據(jù);如果沒有找到匹配項(xiàng)或者有效位無效,則表示該內(nèi)存塊不在Cache中,需要處理器到內(nèi)存中讀取。

Cache之所以能夠提高系統(tǒng)性能,主要是因?yàn)槌绦驁?zhí)行存在局部性現(xiàn)象,即時(shí)間局部性(程序中指令和數(shù)據(jù)在時(shí)間上的關(guān)聯(lián)性,比如:循環(huán)體中的變量和指令)和空間局部性(程序中指令和數(shù)據(jù)在空間上的關(guān)聯(lián)性,比如:列表數(shù)據(jù)結(jié)構(gòu)中的元素)。cache就可以根據(jù)程序的局部性特點(diǎn),以及當(dāng)前執(zhí)行狀態(tài)、歷史執(zhí)行過程、軟件提示等信息,然后以一定的合理方法,在數(shù)據(jù)/指令被使用前取入Cache,也就是cache預(yù)取。

內(nèi)存的數(shù)據(jù)被加載進(jìn)cache后,最終還是需要寫回到內(nèi)存中,這個(gè)寫回的過程存在兩種策略:

直寫(write-through):在CPU對(duì)Cache寫入的同時(shí),將數(shù)據(jù)寫入到內(nèi)存中。這種策略保證了在任何時(shí)刻,內(nèi)存的數(shù)據(jù)和Cache中的數(shù)據(jù)都是同步的,這種方式簡單、可靠。但由于CPU每次對(duì)Cache更新時(shí)都要對(duì)內(nèi)存進(jìn)行寫操作,總線工作繁忙,內(nèi)存的帶寬被大大占用,因此運(yùn)行速度會(huì)受到影響。

回寫(write-back):回寫相對(duì)于直寫而言是一種高效的方法?;貙懴到y(tǒng)通過將Cache line的標(biāo)志位字段添加一個(gè)Dirty標(biāo)志位,當(dāng)處理器在改寫了某個(gè)Cache line后,并不是馬上把其寫回內(nèi)存,而是將該Cache line的Dirty標(biāo)志設(shè)置為1。當(dāng)處理器再次修改該Cache line并且寫回到Cache中,查表發(fā)現(xiàn)該Dirty位已經(jīng)為1,則先將Cache line內(nèi)容寫回到內(nèi)存中相應(yīng)的位置,再將新數(shù)據(jù)寫到Cache中。

除了上述這兩種寫策略,還有WC(write-combining)和UC(uncacheable)。這兩種策略都是針對(duì)特殊的地址空間來使用的,這里不做詳細(xì)討論,有興趣的可以參考Intel官方社區(qū)。

在采用回寫策略的架構(gòu)中,如果多個(gè)CPU同時(shí)對(duì)一個(gè)cache line進(jìn)行修改后的寫回操作,就存在“臟”數(shù)據(jù)區(qū)域的問題,這就是cache一致性問題。其本質(zhì)原因是存在多個(gè)處理器獨(dú)占的Cache,而不是多個(gè)處理器。解決Cache一致性問題的機(jī)制有兩種:基于目錄的協(xié)議(Directory-based protocol)和總線窺探協(xié)議(Bus snooping protocol)。這里因?yàn)槠鶈栴},不再展開討論,有興趣的可參見《深入淺出DPDK》一書相關(guān)內(nèi)容。

事實(shí)上,Cache對(duì)于絕大多數(shù)程序員來說都是透明不可見的,cache完成數(shù)據(jù)緩存的所有操作都是硬件自動(dòng)完成的。但是,硬件也不是完全智能的。因此,Intel體系架構(gòu)引入了能夠?qū)ache進(jìn)行預(yù)取的指令,使一些對(duì)程序執(zhí)行效率有很高要求的程序員能夠一定程度上控制Cache,加快程序的執(zhí)行。DPDK對(duì)cache進(jìn)行預(yù)取操作如下:

while (nb_rx < nb_pkts) { rxdp = &rx_ring[rx_id]; //讀取接收描述符 staterr = rxdp->wb.upper.status_error;//檢查是否有報(bào)文收到 if (!(staterr & rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD)))break; 
rxd = *rxdp; //分配數(shù)據(jù)緩沖區(qū) nmb = rte_rxmbuf_alloc(rxq->mb_pool); 
nb_hold++; //讀取控制結(jié)構(gòu)體 rxe = &sw_ring[rx_id]; …… rx_id++; if (rx_id == rxq->nb_rx_desc) rx_id = 0; //預(yù)取下一個(gè)控制結(jié)構(gòu)體mbuf rte_ixgbe_prefetch(sw_ring[rx_id].mbuf); //預(yù)取接收描述符和控制結(jié)構(gòu)體指針 if ((rx_id & 0x3) == 0) { rte_ixgbe_prefetch(&rx_ring[rx_id]); rte_ixgbe_prefetch(&sw_ring[rx_id]); } …… //預(yù)取報(bào)文 rte_packet_prefetch((char *)rxm->buf_addr + rxm->data_off); //把接收描述符讀取的信息存儲(chǔ)在控制結(jié)構(gòu)體mbuf中 rxm->nb_segs = 1; 
rxm->next = NULL; 
rxm->pkt_len = pkt_len; rxm->data_len = pkt_len; rxm->port = rxq->port_id; 
……rx_pkts[nb_rx++] = rxm; }

同時(shí),DPDK在定義數(shù)據(jù)結(jié)構(gòu)或者數(shù)據(jù)緩沖區(qū)時(shí)就申明cache line對(duì)齊,代碼如下:

#define RTE_CACHE_LINE_SIZE 64 #define __rte_cache_aligned __attribute__((__aligned__(RTE_CACHE_LINE_SIZE)))struct rte_ring_debug_stats { uint64_t enq_success_bulk; uint64_t enq_success_objs; uint64_t enq_quota_bulk; uint64_t enq_quota_objs; uint64_t enq_fail_bulk; uint64_t enq_fail_objs;uint64_t deq_success_bulk; uint64_t deq_success_objs; uint64_t deq_fail_bulk; uint64_t deq_fail_objs; } __rte_cache_aligned;

大頁內(nèi)存

在前文《x86架構(gòu)基礎(chǔ)》一文中提到了TLB的概念,其主要用來緩存內(nèi)存地址轉(zhuǎn)換中的頁表項(xiàng),其本質(zhì)上也是一個(gè)cache,稱之為TLB cache。TLB和cache的區(qū)別是:TLB緩存內(nèi)存地址轉(zhuǎn)換用的頁表項(xiàng),而cache緩存程序用到的數(shù)據(jù)和指令。

TLB中保存著程序線性地址前20位[31:12]和頁框號(hào)的對(duì)應(yīng)關(guān)系,如果匹配到線性地址就可以迅速找到頁框號(hào),通過頁框號(hào)與線性地址后12位的偏移組合得到最終的物理地址。TLB使用虛擬地址進(jìn)行搜索,直接返回對(duì)應(yīng)的物理地址,相對(duì)于內(nèi)存中的多級(jí)頁表需要多次訪問才能得到最終的物理地址,TLB查找大大減少了CPU的開銷。如果需要的地址在TLB Cache中,就會(huì)迅速返回結(jié)果,然后CPU用該物理地址訪問內(nèi)存,這樣的查找操作也稱為TLB命中;如果需要的地址不在TLB Cache中,也就是不命中,CPU就需要到內(nèi)存中訪問多級(jí)頁表,才能最終得到物理地址。但是,TLB的大小是有限的,因此TLB不命中的概率很大,為了提高內(nèi)存地址轉(zhuǎn)換效率,減少CPU的開銷,就提出了大頁內(nèi)存的概念。

在x86架構(gòu)中,一般都分成以下四組TLB:

第一組:緩存一般頁表(4KB頁面)的指令頁表緩存(Instruction-TLB)。

第二組:緩存一般頁表(4KB頁面)的數(shù)據(jù)頁表緩存(Data-TLB)。

第三組:緩存大尺寸頁表(2MB/4MB頁面)的指令頁表緩存(Instruction-TLB)。

第四組:緩存大尺寸頁表(2MB/4MB頁面)的數(shù)據(jù)頁表緩存(Data-TLB)

如果采用常規(guī)頁(4KB)并且使TLB總能命中,需要尋址的內(nèi)容都在該內(nèi)容頁內(nèi),那么至少需要在TLB表中存放兩個(gè)表項(xiàng)。如果一個(gè)程序使用了512個(gè)內(nèi)容頁也就是2MB大小,那么需要512個(gè)頁表表項(xiàng)才能保證不會(huì)出現(xiàn)TLB不命中的情況。但是,如果采用2MB作為分頁的基本單位,那么只需要一個(gè)表項(xiàng)就可以保證不出現(xiàn)TLB不命中的情況;對(duì)于消耗內(nèi)存以GB為單位的大型程序,可以采用1GB為單位作為分頁的基本單位,減少TLB不命中的情況。需要注意的是:系統(tǒng)能否支持大頁,支持大頁的大小為多少是由其使用的處理器決定的。

在Linux啟動(dòng)之后,如果想預(yù)留大頁,則可以使用以下的方法來預(yù)留內(nèi)存。在非NUMA系統(tǒng)中,可以使用以下方法預(yù)留2MB大小的大頁。

# 預(yù)留1024個(gè)大小為2MB的大頁,也就是預(yù)留了2GB內(nèi)存。

echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

系統(tǒng)未開啟大頁內(nèi)存的狀態(tài)

系統(tǒng)開啟大頁內(nèi)存后的狀態(tài)

如果是在NUMA系統(tǒng)中,假設(shè)有兩個(gè)NODE的系統(tǒng)中,則可以用以下的命令:

# 在NODE0和NODE1上各預(yù)留1024個(gè)大小為2MB的大頁,總共預(yù)留了4GB大小。echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages echo 1024 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages 

而對(duì)于大小為1GB的大頁,則必須在Linux的GRUB配置文件中進(jìn)行修改,并重啟系統(tǒng)生效,不能動(dòng)態(tài)預(yù)留。

DPDK中也是使用HUGETLBFS來使用大頁。首先,它需要把大頁mount到某個(gè)路徑,比如/mnt/huge,以下是命令:

mkdir /mnt/huge mount -t hugetlbfs nodev /mnt/huge

需要注意的是:在mount之前,要確保之前已經(jīng)成功預(yù)留內(nèi)存,否則會(huì)失敗。該命令只是臨時(shí)的mount了文件系統(tǒng),如果想每次開機(jī)時(shí)省略該步驟,可以修改/etc/fstab文件,加上如下一行:

nodev /mnt/huge hugetlbfs defaults 0 0

對(duì)于1GB大小的大頁,則必須用如下的命令:

nodev /mnt/huge_1GB hugetlbfs pagesize=1GB 0 0

然后,在DPDK運(yùn)行的時(shí)候,會(huì)使用mmap()系統(tǒng)調(diào)用把大頁映射到用戶態(tài)的虛擬地址空間,然后就可以正常使用了。

未完待續(xù)......

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

移動(dòng)Labs是中國移動(dòng)的社交化新媒體平臺(tái),是面向外部行業(yè)及產(chǎn)業(yè)鏈合作伙伴的信息發(fā)布、業(yè)務(wù)發(fā)展和產(chǎn)業(yè)推進(jìn)門戶。