DPDK優(yōu)化點(diǎn)和NUMA架構(gòu)

這篇博客先介紹在dpdk中使用到的一些優(yōu)化點(diǎn)[后期如果遇到其他的會(huì)完善],然后是NUMA架構(gòu)找都,看了官方說(shuō)明唇辨,對(duì)于10Gbit/s光口,能每秒發(fā)送/接收1480w+的64Byte[以太幀頭+ip頭+tcp頭+數(shù)據(jù)]的數(shù)據(jù)包能耻,為什么性能這么好?一方面與我們平時(shí)編碼習(xí)慣相關(guān),另一方面dpdk的源碼是值得研究的。以下列出的優(yōu)化點(diǎn)與具體的網(wǎng)卡性能方面的優(yōu)化不一樣骂际,后者是個(gè)有挑戰(zhàn)性的事情尖殃,下面的一些點(diǎn)可以使用到與dpdk無(wú)關(guān)的開(kāi)發(fā)中。

  1. likely和unlikely的使用
    在dpdk的example中劲绪,很多語(yǔ)句使用了如下的函數(shù):
153    /* Send burst of TX packets, to second port of pair. */
154    const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
155          bufs, nb_rx);
156 
157    /* Free any unsent packets. */
158    if (unlikely(nb_tx < nb_rx)) {
159          uint16_t buf;
160          for (buf = nb_tx; buf < nb_rx; buf++)
161                rte_pktmbuf_free(bufs[buf]);
162    }
253    /* Read packet from the ring */
254    nb_pkt = rte_ring_sc_dequeue_burst(conf->rx_ring, (void **)mbufs,
255           burst_conf.ring_burst);
256    if (likely(nb_pkt)) {
257          int nb_sent = rte_sched_port_enqueue(conf->sched_port, mbufs,
258              nb_pkt);
259 
260          APP_STATS_ADD(conf->stat.nb_drop, nb_pkt - nb_sent);
261          APP_STATS_ADD(conf->stat.nb_rx, nb_pkt);
262    }
126 /* Branch prediction helpers. */
127 #ifndef likely
128 #define likely(c) __builtin_expect(!!(c), 1)
129 #endif              
130 #ifndef unlikely
131 #define unlikely(c) __builtin_expect(!!(c), 0)
132 #endif

其中__builtin_expect原型如下:
long __builtin_expect(long exp, long c);!!(c)的效果是得到一個(gè)布爾值,該函數(shù)的作用是更好的分支預(yù)測(cè)洪燥;使用likely() ,執(zhí)行if后面的語(yǔ)句的機(jī)會(huì)更大乳乌,使用unlikely()捧韵,執(zhí)行else后面的語(yǔ)句的機(jī)會(huì)更大,原理“It optimizes things by ordering the generated assembly code correctly, to optimize the usage of the processor pipeline. To do so, they arrange the code so that the likeliest branch is executed without performing any jmp instruction (which has the bad effect of flushing the processor pipeline).”
主要是分支預(yù)測(cè)失誤汉操,指令的跳轉(zhuǎn)帶來(lái)的性能會(huì)下降很多再来。為什么呢?從《深入理解計(jì)算機(jī)系統(tǒng)》書(shū)上摘取,p141:“另一方面芒篷,錯(cuò)誤預(yù)測(cè)一個(gè)跳轉(zhuǎn)要求處理器丟掉它為該跳轉(zhuǎn)指令后所有指令已經(jīng)做了的工作搜变,然后再開(kāi)始用從正確位置處起始的指令去填充流水線,大約會(huì)浪費(fèi)20?40個(gè)時(shí)鐘周期”针炉;從匯編代碼層面理解參考文末第一個(gè)引用挠他。

  1. 指令預(yù)取
    這里僅介紹的cache數(shù)據(jù)預(yù)取[空間局部性和時(shí)間局部性]。分為硬件預(yù)取和軟件預(yù)取篡帕,前者根據(jù)不同的架構(gòu)如NetBurst殖侵,由底層硬件預(yù)取單元根據(jù)一定條件自動(dòng)激活預(yù)取,“一定的條件”比較復(fù)雜赂苗,比如讀取的數(shù)據(jù)是回寫(xiě)的內(nèi)存模型愉耙;沒(méi)有連續(xù)的存儲(chǔ)指令等,但有時(shí)并不一定能提高程序的執(zhí)行效率拌滋。如在訪問(wèn)的數(shù)據(jù)結(jié)構(gòu)沒(méi)有規(guī)律的情況下朴沿,那么硬件預(yù)取會(huì)占用更多的帶寬,浪費(fèi)一級(jí)cache的空間败砂,淘汰了程序本身存放在一級(jí)cache中的數(shù)據(jù)赌渣。
    用的較多的是軟件預(yù)取,由指令PREFETCH0~PREFETCH2,PREFETCHNTA組成昌犹,在開(kāi)發(fā)過(guò)程中坚芜,把即將用到的數(shù)據(jù)從內(nèi)存中加載到cache,后期直接命中cache斜姥,減小了從內(nèi)存直接讀取的延遲鸿竖,減小了處理器的等待時(shí)間。
    對(duì)于一級(jí)cache铸敏,延遲為3?5個(gè)指令周期缚忧,二級(jí)cache為十幾個(gè)指令周期,三級(jí)cache為幾十個(gè)杈笔。
    在dpdk例子中闪水,常看到這樣的語(yǔ)句:
341      /* for traffic we receive, queue it up for transmit */
342      uint16_t i;
343      rte_prefetch_non_temporal((void *)bufs[0]);
344      rte_prefetch_non_temporal((void *)bufs[1]);
345      rte_prefetch_non_temporal((void *)bufs[2]);
346      for (i = 0; i < nb_rx; i++) {
347           struct output_buffer *outbuf;
348           uint8_t outp;
349           rte_prefetch_non_temporal((void *)bufs[i + 3]);
350           /*
351           * workers should update in_port to hold the
352           * output port value
353           */
354           outp = bufs[i]->port;
355           /* skip ports that are not enabled */
356           if ((enabled_port_mask & (1 << outp)) == 0)
357               continue;
358 
359           outbuf = &tx_buffers[outp];
360           outbuf->mbufs[outbuf->count++] = bufs[i];
361           if (outbuf->count == BURST_SIZE)
362               flush_one_port(outbuf, outp);
363      }      

rte_prefetch_non_temporal的功能與PREFETCH0對(duì)應(yīng)蒙具,即將數(shù)據(jù)存放在每一級(jí)cache中球榆,在使用完一次后是可以被淘汰出動(dòng)的〗ぃ“Prefetch a cache line into all cache levels (non-temporal/transient version)
The non-temporal prefetch is intended as a prefetch hint that processor will use the prefetched data only once or short period, unlike the rte_prefetch0() function which imply that prefetched data to use repeatedly.”
其實(shí)現(xiàn)是內(nèi)嵌匯編代碼:

 42 static inline void rte_prefetch0(const volatile void *p)
 43 {               
 44     asm volatile ("pld [%0]" : : "r" (p));
 45 }

 57 static inline void rte_prefetch_non_temporal(const volatile void *p)
 58 {
 59     /* non-temporal version not available, fallback to rte_prefetch0 */
 60     rte_prefetch0(p);
 61 }
  1. cache相關(guān)
    主要點(diǎn)是cache line持钉,example中好些結(jié)構(gòu)聲明末尾有如下形式:
106 struct lcore_queue_conf {
107     unsigned n_rx_port;
108     unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE];
109 } __rte_cache_aligned;
#define RTE_CACHE_LINE_SIZE 64
#define __rte_cache_aligned __attribute__((__aligned__(RTE_CACHE_LINE_SIZE)))

就是定義一個(gè)變量時(shí)要按照cache line對(duì)齊,這樣做的好處是一方面防止多線程中的false sharing而導(dǎo)致“抖動(dòng)”問(wèn)題融师,影響性能右钾,另一方面如果變量所占空間跨line,則可能需要幾次從內(nèi)存加載和存儲(chǔ),再然后呢要求變量地址cache line對(duì)齊可能會(huì)浪費(fèi)點(diǎn)內(nèi)存空間舀射。
cache line有四種狀態(tài)窘茁,分別是modified,exclusive, shared, invalid脆烟,即MESI協(xié)議山林,為了解決一致性問(wèn)題而出現(xiàn)的。很多理論性的可以查下wiki邢羔,我也記不清楚驼抹。“在dpdk中拜鹤,避免多個(gè)核訪問(wèn)同一個(gè)內(nèi)存地址或者數(shù)據(jù)結(jié)構(gòu)框冀。這樣,每個(gè)核都避免與其他核共享數(shù)據(jù)敏簿,減少因?yàn)殄e(cuò)誤的數(shù)據(jù)共享而導(dǎo)致cache一致性的開(kāi)銷明也。”
具體cache相關(guān)的基礎(chǔ)知識(shí)點(diǎn)可參考《深入理解計(jì)算機(jī)系統(tǒng)》惯裕,理解cache的工作原理温数,能寫(xiě)出較高效的代碼。

  1. 本地內(nèi)存
    這個(gè)點(diǎn)與NUMA架構(gòu)有關(guān)蜻势,在此之前撑刺,有必要解釋下SMP架構(gòu)。
    SMP(Symmetric Multi Processing)握玛,即對(duì)稱多處理系統(tǒng)够傍,特點(diǎn)如下:
    a)所有的CPU共享全部資源,如總線挠铲,內(nèi)存和I/O系統(tǒng)等王带;
    b)所有處理器都是平等的,沒(méi)有主從關(guān)系市殷;
    c)內(nèi)存是統(tǒng)一結(jié)構(gòu),統(tǒng)一尋址的(UMA:Uniform Memory Access);
    d)cpu和內(nèi)存刹衫,cpu之間是通過(guò)一條總線連接起來(lái)的醋寝;
    但有以下缺點(diǎn):
    a)擴(kuò)展能力非常有限;
    b)隨著cpu個(gè)數(shù)增加带迟,系統(tǒng)總線成為了瓶頸音羞,cpu與內(nèi)存之間的通信延遲加大;
    故出現(xiàn)了NUMA(Non Uniform Memory Access Architecture)仓犬,即非一致存儲(chǔ)器訪問(wèn)嗅绰,特點(diǎn)如下:
    a)cpu和本地內(nèi)存擁有更小的延遲與更大的帶寬;
    b)整個(gè)內(nèi)存可作為一個(gè)整體,任何cpu都能訪問(wèn)窘面,跨本地內(nèi)存訪問(wèn)較訪問(wèn)本地內(nèi)存慢一些翠语;
    c)每個(gè)cpu可以有本地總線,和內(nèi)存一樣财边,訪問(wèn)本地總線延遲低肌括,添吐率高;
    兩者如下圖所示:
numa.png

而在dpdk中的example使用中酣难,可以看見(jiàn)如下代碼使用方式:

150 /* helper to create a mbuf pool */
151 struct rte_mempool *
152 rte_pktmbuf_pool_create(const char *name, unsigned n,
153     unsigned cache_size, uint16_t priv_size, uint16_t data_room_size,
154     int socket_id)

根據(jù)該線程所在的socket_id去創(chuàng)建內(nèi)存谍夭;

1183 int
1184 rte_eth_rx_queue_setup(uint8_t port_id, uint16_t rx_queue_id,
1185                uint16_t nb_rx_desc, unsigned int socket_id,
1186                const struct rte_eth_rxconf *rx_conf,
1187                struct rte_mempool *mp)

1265 int
1266 rte_eth_tx_queue_setup(uint8_t port_id, uint16_t tx_queue_id,
1267                uint16_t nb_tx_desc, unsigned int socket_id,
1268                const struct rte_eth_txconf *tx_conf)

根據(jù)該線程所在的socket_id去創(chuàng)建端口的收發(fā)隊(duì)列;

161 struct rte_ring *
162 rte_ring_create(const char *name, unsigned count, int socket_id, unsigned flags)

根據(jù)該線程所在的socket_id去創(chuàng)建無(wú)鎖環(huán)形buffer憨募。

還有無(wú)鎖的使用紧索,cpu親和性,ddio菜谣,內(nèi)存交叉訪問(wèn)等...

參考:
https://kernelnewbies.org/FAQ/LikelyUnlikely
http://docs.oracle.com/cd/E19253-01/819-7057/6n91f8su6/index.html
http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Memory/direct.html
https://en.wikipedia.org/wiki/MESI_protocol
https://en.wikipedia.org/wiki/False_sharing
https://en.wikipedia.org/wiki/Symmetric_multiprocessing
https://en.wikipedia.org/wiki/Non-uniform_memory_access
http://www.tuicool.com/articles/j6vY7nq

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末珠漂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子葛菇,更是在濱河造成了極大的恐慌甘磨,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眯停,死亡現(xiàn)場(chǎng)離奇詭異济舆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)莺债,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門滋觉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人齐邦,你說(shuō)我怎么就攤上這事椎侠。” “怎么了措拇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵我纪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我丐吓,道長(zhǎng)浅悉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任券犁,我火速辦了婚禮术健,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘粘衬。我一直安慰自己荞估,他們只是感情好咳促,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著勘伺,像睡著了一般跪腹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娇昙,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天尺迂,我揣著相機(jī)與錄音,去河邊找鬼冒掌。 笑死噪裕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的股毫。 我是一名探鬼主播膳音,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼铃诬!你這毒婦竟也來(lái)了祭陷?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤趣席,失蹤者是張志新(化名)和其女友劉穎兵志,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體宣肚,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡想罕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了霉涨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片按价。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖笙瑟,靈堂內(nèi)的尸體忽然破棺而出楼镐,到底是詐尸還是另有隱情,我是刑警寧澤往枷,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布框产,位于F島的核電站,受9級(jí)特大地震影響错洁,放射性物質(zhì)發(fā)生泄漏茅信。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一墓臭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧妖谴,春花似錦窿锉、人聲如沸酌摇。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窑多。三九已至,卻和暖如春洼滚,著一層夾襖步出監(jiān)牢的瞬間埂息,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工遥巴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留千康,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓铲掐,卻偏偏與公主長(zhǎng)得像拾弃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摆霉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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