DPDK性能優(yōu)化技術(shù)匯總

持續(xù)更新中笆凌。

Memory Access


Address Alignment

在內(nèi)存中存取一個(gè)變量最高效的方式是將其放在一個(gè)可以被它的長度整除的地址上蕴侣。

(void *)&variable % sizeof(variable) == 0

所謂的按某個(gè)長度對齊就是這個(gè)意思捣炬。GCC編譯器會自動幫我們處理這些事情蛋欣。比較特殊的方式是將一個(gè)大型的結(jié)構(gòu)體丐巫,或者靜態(tài)數(shù)組按64byte的方式對齊:

int BigArray[1024] __attribute__((aligned(64)));

這主要是考慮到CPU的Cache Line長度多為64byte锨侯,變量按64對齊可以使其正好開始于一個(gè)Cache Line,減少Cache Miss/False Sharing以及利用CPU的高級指令集并行計(jì)算捂刺。

Note: _ attribute _((aligned(x)))有時(shí)只對全局變量有效谣拣,而對局部變量無效。

Huge Page

大頁技術(shù)是當(dāng)前流行的一種性能優(yōu)化技術(shù)族展。在Linux系統(tǒng)中有一套復(fù)雜的進(jìn)程虛擬地址和內(nèi)存物理地址的轉(zhuǎn)換機(jī)制森缠,復(fù)雜的細(xì)節(jié)我們不去關(guān)心,只需要知道Linux是通過頁(Page)這一機(jī)制(Look-up table)來確立兩者的對應(yīng)關(guān)系的仪缸。簡單的類比就是在一本2000頁的書中找到某一個(gè)章節(jié)贵涵,遠(yuǎn)比在一本2頁的書中復(fù)雜∏』考慮到傳統(tǒng)頁面4KB的大小和大頁2GB的小大之差宾茂,這個(gè)類比還不是那么恰當(dāng)。
在CPU中拴还,需要以緩存的形式存儲一些轉(zhuǎn)換關(guān)系跨晴,這種緩存成為TLB Cache。使用大頁可以減少TLB Cache Miss片林。


Virtual addr maps to physical addr

Note: Huge Page可以在絕大部分情況之下提高性能端盆,但并不是所有情況下都可以起到提升性能的效果树瞭。對于內(nèi)存,需要綜合考慮各種因素爱谁,提高性能的基本策略還是以空間換時(shí)間。詳細(xì)的分析文章請Click

NUMA

嚴(yán)格來說NUMA并不是一種性能優(yōu)化技術(shù)孝偎,而是一種內(nèi)存架構(gòu)访敌。


NUMA Architecture

每一個(gè)CPU Core都與它本地連接的內(nèi)存直接相連,獨(dú)享總線衣盾,具有最快的讀寫速度寺旺。如果去遠(yuǎn)程(remote)內(nèi)存去讀寫的話,則需要跨CPU Core執(zhí)行势决。在DPDK中阻塑,有一整套精巧且高效的內(nèi)存分配和管理機(jī)制,結(jié)合大頁和NUMA等機(jī)制果复,基本原則是陈莽,將一個(gè)CPU Core需要處理的數(shù)據(jù)都放在離它最近的內(nèi)存上。
相關(guān)的實(shí)現(xiàn)可以參考DPDK中memseg memzone等內(nèi)存機(jī)制相關(guān)代碼的實(shí)現(xiàn)虽抄,這里可以有專門文章介紹走搁。

Polling Mode Drive(PMD)

是DPDK實(shí)現(xiàn)的優(yōu)化Linux網(wǎng)絡(luò)接收發(fā)送性能的模塊,官方有詳細(xì)的介紹資料迈窟。Click

Memory Pool

對于需要頻繁填充釋放的內(nèi)存空間可以采用內(nèi)存池的方式預(yù)先動態(tài)分配一整塊內(nèi)存區(qū)域,然后統(tǒng)一進(jìn)行管理,從而省去頻繁的動態(tài)分配和釋放過程锄贼,既提高了性能榴都,同時(shí)也減少了內(nèi)存碎片的產(chǎn)生。
內(nèi)存池多以隊(duì)列的形式組織空閑或占用內(nèi)存湖员。在DPDK中贫悄,還考慮了地址對齊,以及CPU core local cache等因素破衔,以提升性能清女。

這里提到的內(nèi)存對齊不同于前面僅僅將變量放在一個(gè)合適的地址“數(shù)目”上,而是綜合考慮了內(nèi)存通道(channel)和rank晰筛,將變量(比如一個(gè)三層網(wǎng)絡(luò)的Pkt)嫡丙,平均分布于不同的channel之上(多依靠padding),可以減少channel擁塞读第,顯著提升性能曙博。如圖:


Two Channels and Quad-ranked DIMM Example

對于供多個(gè)線程同時(shí)使用的內(nèi)存池,為了減少對內(nèi)存池的讀寫沖突怜瞒,可以考慮Local Cache的機(jī)制父泳。即內(nèi)存池為每一個(gè)線程/CPU Core維護(hù)一個(gè)Local Cache般哼,本地的CPU Core對其操作是沒有競爭的。每個(gè)CPU Core都是以bulk的形式從內(nèi)存池中請求數(shù)據(jù)寫入Local Cache或者將Local Cache的數(shù)據(jù)寫入內(nèi)存池惠窄。這樣便大幅減少了讀寫沖突蒸眠。


Local cache per core

Linker Considerations

可以理解的一個(gè)簡單事實(shí)是,如果經(jīng)常使用的函數(shù)被儲存在指令內(nèi)存的同一區(qū)域杆融,甚至存儲順序和調(diào)用順序一致楞卡,那么程序整體執(zhí)行的效率將會有所提升。
一個(gè)簡單的方法就是盡量使用static函數(shù):

static void f(void)

將同一模塊中的函數(shù)在鏈接階段放在一起脾歇。但很多時(shí)候蒋腮,處于程序模塊化編程考慮,模塊之間互相調(diào)用的函數(shù)和方法并沒有被顯式地置于同一處指令內(nèi)存藕各,此時(shí)可以在關(guān)鍵函數(shù)集中采用:

_attaribute_(section(X))

將函數(shù)顯式地置于Read only(program memory)Section X池摧,方便一起調(diào)用。同時(shí)也可以map文件的形式安排指定激况。

Note: 同樣的原則也適用于變量作彤。

CPU


Advanced Instruction Set

使用先進(jìn)的CPU指令集,帶來的主要好處是可以并行完成向量化的操作乌逐,也就是所謂的SIMD(Single-Instruction-Multiple-Data)操作宦棺。
當(dāng)需要對大型數(shù)據(jù)集執(zhí)行相同的操作的時(shí)候,向量操作可以帶來明顯的性能提升黔帕。例如圖像處理代咸、大型矩陣計(jì)算、網(wǎng)絡(luò)數(shù)據(jù)包還有內(nèi)存復(fù)制操作等成黄。

Note: 對于數(shù)據(jù)間有互相依賴和操作上有繼承的運(yùn)算呐芥,比如排序,并不適合向量操作奋岁。

先進(jìn)的指令集一般包括SSE SSE2 AVX AVX512 YMM ZMM思瘟。這些指令集對數(shù)據(jù)儲存的地址都有比較嚴(yán)格的要求,比如256bit-YMM要求數(shù)據(jù)按32對齊闻伶,512-bit ZMM要求數(shù)據(jù)按64對齊滨攻。
對于向量操作,一般希望符合如下條件:

  • 小型的數(shù)據(jù)類型:char short int float
  • 對大型數(shù)據(jù)集執(zhí)行類似的操作
  • 數(shù)據(jù)對齊
  • 數(shù)據(jù)集長度可以被向量長度整除

Note: 可以將關(guān)鍵函數(shù)寫為針對不同數(shù)據(jù)集的不同版本蓝翰,視運(yùn)行環(huán)境編譯運(yùn)行光绕。

Compiler


Branch Predication


CPU是以流水線的方式執(zhí)行程序指令。所謂流水線畜份,可以簡單理解為在執(zhí)行一個(gè)指令的同時(shí)诞帐,讀取下一條指令。對于程序中大量出現(xiàn)的if else while for ? :等含有條件判斷的情景爆雹,CPU需要能夠正確提取下一條指令以便流水線可以流暢執(zhí)行下去停蕉。一旦提取的是錯(cuò)誤分支的指令愕鼓,雖然不影響程序運(yùn)行的結(jié)果,但整條流水線都會被清空慧起,再重新讀入正確分支的指令菇晃,對程序運(yùn)行效率影響頗大。
CPU一般都有硬件分支預(yù)測器蚓挤,但我們也可以用likely()/unlikely()等方式顯示指定谋旦,另外在設(shè)計(jì)程序的時(shí)候也以使分支判斷具有一定的規(guī)律性為好,比如一組經(jīng)過排序的輸入數(shù)據(jù)屈尼。

Branchless Code

為了最大限度減小Branch mispredication對性能帶來的影響,可以將一些常見的分支判斷轉(zhuǎn)換為Branchless的形式拴孤。比如返回兩個(gè)數(shù)中較大的值脾歧,一般可以寫做:

int max = (x > y) ? x : y;

這里其實(shí)隱含了一個(gè)條件判斷。如果用branchless的形式演熟,同樣的功能可以寫做為:

int max = x ^ ((x ^ y) & -(x < y));

當(dāng)有大量調(diào)用鞭执,同時(shí)輸入無甚規(guī)律性的時(shí)候可以考慮采用Branchless code。一個(gè)比較全面的技巧合計(jì)在:Click芒粹。

loop-unrolling

Loop-unrolling的一大好處就是可以減少循環(huán)分支預(yù)測的次數(shù)兄纺。對于簡單的循環(huán),CPU其實(shí)可以很好的完成分支預(yù)測的工作化漆,但對于嵌套的循環(huán)估脆,或者循環(huán)內(nèi)部會改變循環(huán)次數(shù)的循環(huán),分支預(yù)測就變得困難座云。loop-unrolling的特點(diǎn)可以用如下的例子說明:

int i;
for (i = 0; i < 20; i++) {
    if (i % 2 == 0) {
        FuncA(i);
    } else {
        FuncB(i);
    }
    FuncC(i);
}

上面這個(gè)執(zhí)行了20次的循環(huán)可以用loop-unrolling展開:

int i;
for (i = 0; i < 20; i += 2) {
    FuncA(i);
    FuncC(i);
    FuncB(i + 1);
    FuncC(i + 1);
}
  • Cons:

  • 循環(huán)只執(zhí)行10次疙赠,減少了一半

  • 可以更準(zhǔn)確得被CPU的branch predictor預(yù)測

  • 沒有了循環(huán)體內(nèi)的if分支

  • Pros

  • 如果循環(huán)計(jì)數(shù)器是奇數(shù),則需要特別的處理朦拖。

Note: Loop-unrolling也需要考慮適用場合圃阳。主要適用于循環(huán)體的分支是主要的性能熱點(diǎn)的時(shí)候。

Anti-aliasing

當(dāng)有多個(gè)指針指向同一處物理內(nèi)存(變量)的時(shí)候璧帝,稱為pointer aliasing捍岳。作為編譯器,并不能確認(rèn)兩個(gè)相同類型的指針是否指向同一處地址睬隶,即對其他指針的操作锣夹,是否會影響另外的指針?biāo)赶虻膬?nèi)存。這就要求每次碰到這兩個(gè)指針其中的任何一個(gè)的時(shí)候苏潜,都需要重新從內(nèi)存中讀取當(dāng)前值晕城。示例如下:

void Func1 (int a[], int *p) {
    int i;
    for (i = 0; i < 100; i++) {
        a[i] = *p + 2;
    }
}

void Func2() {
    int list[100];
    Func1(list, &list[8]);
}

Func1中,有必要每次都重新讀入*p窖贤,并且重新計(jì)算*p + 2砖顷,因?yàn)樵?code>Func2的調(diào)用中贰锁,與list[8]發(fā)生aliasing。對編譯器來講滤蝠,它需要考慮這種“理論上的可能”豌熄,從而付出大量的重復(fù)勞動。

當(dāng)程序可以確認(rèn)兩個(gè)指針不會發(fā)生aliasing的時(shí)候物咳,可以用關(guān)鍵字__restrict__給編譯器以明確的指示锣险。

Prefetch

使用prefetch指令可以幫助我們提前預(yù)存一個(gè)將要使用的變量至CPU緩存:

_mm_prefetch

但在實(shí)際使用過程中要特別小心,現(xiàn)代CPU都有自己的硬件prefetch機(jī)制览闰,如果不是經(jīng)過測試芯肤,確認(rèn)性能有所提高,盡量不要輕易使用該指令压鉴。這里有一篇資料對此有詳細(xì)解釋:Click

Note:需要確認(rèn)CPU支持SSE指令集

Multi-threads


Lock-less

一般將GCC提供的一些原子操作視為“Lock-less code”崖咨。這些操作包括一些原子自增,CAS等操作油吭。

__sync_fetch_and_add(type* ptr, type value)
__sync_compare_and_swap(type *ptr, type oldval,  type newval)
etc...

這些操作雖然表面上沒有了鎖的痕跡击蹲,但實(shí)際上其匯編指令還是存在一個(gè)#lock鎖總線的操作。所以也不必對其性能抱太高期望婉宰。對于所有關(guān)于鎖的操作歌豺,需要強(qiáng)調(diào)的是,鎖本身并不影響性能心包,只有對鎖的爭搶才影響性能类咧。

Local Cache

如同之前介紹的那樣,還有一種策略是將任務(wù)盡量劃分為不相互依賴的各部分蟹腾,分別交給不同的CPU Core去處理轮听,僅僅在結(jié)果匯總的時(shí)候有少量的鎖操作。在DPDK中大量應(yīng)用了這種思想岭佳。

Core Affinity

將一個(gè)任務(wù)指定交給某個(gè)CPU Core處理血巍,可以減少上下文切換和context switch的次數(shù),以及提高緩存命中率珊随。在Linux程序中可以通過

int sched_setaffinity(pid_t  pid, size_t cpusetsize, const cpu_set_t *mask);

來設(shè)定線程的CPU親和性述寡。

False Sharing

False Sharing也是在多線程操作中需要避免的緩存失效的問題。如果兩個(gè)變量分別被兩個(gè)線程操作叶洞,但它們出現(xiàn)在同一條Cache Line中鲫凶,則兩個(gè)線程之間還是會互相影響。任何一個(gè)線程對該Cache Line的寫操作衩辟,都會失整條Cache Line在另外一個(gè)線程處失效螟炫。如下圖:



對此最簡單的辦法,是可以添加Cache Padding將兩個(gè)變量分隔在不同的Cache Line之中艺晴,或者以Cache Line Size對齊的方式分配內(nèi)存昼钻。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掸屡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子然评,更是在濱河造成了極大的恐慌仅财,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碗淌,死亡現(xiàn)場離奇詭異盏求,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)亿眠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門碎罚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纳像,你說我怎么就攤上這事荆烈。” “怎么了爹耗?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谜喊。 經(jīng)常有香客問我潭兽,道長,這世上最難降的妖魔是什么斗遏? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任山卦,我火速辦了婚禮,結(jié)果婚禮上诵次,老公的妹妹穿的比我還像新娘账蓉。我一直安慰自己,他們只是感情好逾一,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布铸本。 她就那樣靜靜地躺著,像睡著了一般遵堵。 火紅的嫁衣襯著肌膚如雪箱玷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天陌宿,我揣著相機(jī)與錄音锡足,去河邊找鬼。 笑死壳坪,一個(gè)胖子當(dāng)著我的面吹牛舶得,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爽蝴,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼沐批,長吁一口氣:“原來是場噩夢啊……” “哼纫骑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起珠插,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤惧磺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后捻撑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磨隘,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年顾患,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了番捂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡江解,死狀恐怖设预,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情犁河,我是刑警寧澤鳖枕,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站桨螺,受9級特大地震影響宾符,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灭翔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一魏烫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肝箱,春花似錦哄褒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骏融,卻和暖如春罚舱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绎谦。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工管闷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人窃肠。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓包个,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子碧囊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容