VPP tips
1.性能從何而來。
原文鏈接:
http://www.360doc.com/content/18/0428/20/53742993_749517107.shtml
https://steeven.iteye.com/blog/2347150 DPDK代碼級別性能優化總結
https://www.jianshu.com/p/346bf99b2fb1
https://www.jianshu.com/p/ed914b24f6da
https://blog.csdn.net/Dgh19940/article/details/79603843
架構角度:DPDK的巨頁、NUMA、D-cache優化,VPP 的I-cache優化;
算法角度:Bihash,查表lockless;
代碼角度:Vector、宏構造函數、結構體cacheline對齊、線程綁核、指令預取、指令優化;
2.路由查找
https://blog.csdn.net/dog250/article/details/6596046
Internet路由之路由表查找算法概述-哈希/LC-Trie樹/256-way-mtrie樹
3. __constructor__修飾符
通過一個簡單的例子介紹一下gcc的__attribute__ ((constructor))屬性的作用。gcc允許為函數設置__attribute__ ((constructor))和__attribute__ ((destructor))兩種屬性,顧名思義,就是將被修飾的函數作為構造函數或析構函數。程序員可以通過類似下面的方式為函數設置這些屬性:
void funcBeforeMain() __attribute__ ((constructor));
void funcAfterMain() __attribute__ ((destructor));
4.unlikely
CPU是以流水線的方式執行程序指令。所謂流水線,可以簡單理解為在執行一個指令的同時,讀取下一條指令。對於程序中大量出現的if else while for ? :等含有條件判斷的情景,CPU需要能夠正確提取下一條指令以便流水線可以流暢執行下去。一旦提取的是錯誤分支的指令,雖然不影響程序運行的結果,但整條流水線都會被清空,再重新讀入正確分支的指令,對程序運行效率影響頗大。
CPU一般都有硬件分支預測器,但我們也可以用likely()/unlikely()等方式顯示指定,另外在設計程序的時候也以使分支判斷具有一定的規律性為好,比如一組經過排序的輸入數據。
為了最大限度減小Branch mispredication對性能帶來的影響,可以將一些常見的分支判斷轉換為Branchless的形式。
鏈接:https://www.jianshu.com/p/ed914b24f6da
long __builtin_expect(long exp, long c);!!(c)的效果是得到一個布爾值,該函數的作用是更好的分支預測;使用likely() ,執行if后面的語句的機會更大,使用unlikely(),執行else后面的語句的機會更大,原理“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).”
主要是分支預測失誤,指令的跳轉帶來的性能會下降很多。為什么呢?從《深入理解計算機系統》書上摘取,p141:“另一方面,錯誤預測一個跳轉要求處理器丟掉它為該跳轉指令后所有指令已經做了的工作,然后再開始用從正確位置處起始的指令去填充流水線,大約會浪費20〜40個時鍾周期”;從匯編代碼層面理解參考文末第一個引用。
鏈接:https://www.jianshu.com/p/346bf99b2fb1
5.巨頁hugepage
https://www.cnblogs.com/small-office/p/9766536.html 綁核與巨頁
https://www.cnblogs.com/031602523liu/p/10537694.html UIO、巨頁、CPU親和性、NUMA簡介
https://blog.csdn.net/qq_33611327/article/details/81738195 mmap分析
系統能否支持大頁,支持大頁的大小為多少是由其使用的處理器決定的。
大頁預留之后,接下來則涉及使用的問題。DPDK使用HUGETLBFS來使用大頁。首先,它需要把大頁mount到某個路徑 比如 /mnt/huge,或者/dev/hugepages/,接下來,DPDK運行的時候,會使用mmap()系統調用把大頁映射到用戶態的虛擬地址空間,然后就可以正常使用了。
由於DPDK是運行在用戶空間,而巨頁是在內核態的,因此通過mmap實現了用戶空間到內核空間的快速訪問。
6. cpu親和性
https://www.cnblogs.com/031602523liu/p/10537694.html
pthread_setaffinity_np 設置線程運行在特定CPU核上
CPU的親和性也就是cpu affinity機制,指的是進程要在指定的 CPU 上盡量長時間地運行而不被遷移到其他處理器, 通過處理器關聯可以將虛擬處理器映射到一個物理處理器上 ,也就是說把一個程序綁定到一個物理CPU上。
而且在多核運行的機器上,每個CPU本身自己會有緩存,緩存着進程使用的信息,而進程可能會被OS調度到其他CPU上,如此,CPU cache命中率就低了。當一個進程或線程綁定CPU后,程序就會一直在指定的cpu跑,不會由操作系統調度到其他CPU上,減少了cache miss,提高性能和效率。
7. D-cache & I-cache
7.1 I-Cache
VPP按組處理報文,解決I-cache抖動問題。
7.2 D-Cache
原文鏈接:https://blog.csdn.net/Dgh19940/article/details/79603843 DPDK中的Cache優化
1)寫接受描述符到內存,填充數據緩沖區指針,網卡接收到報文后就根據該地址把報文內容填進去。
2)從內存中讀取接收描述符(到接收到報文時,網卡會更新該結構)(內存讀),從而確認是否收到報文。
3)從接收描述符確認收到報文時,從內存中讀取控制結構體的指針,再從內存中讀取控制結構體,把從接收描述符中讀取的信息填充到該控制結構體(內存讀)。
4)更新接收隊列寄存器,表示軟件接收到了新的報文。
5)從內存讀取報文頭部(內存讀),決定轉發端口。
6)從控制結構體把報文信息填入到發送隊列發送描述符中,更新發送隊列寄存器。
7)從內存中讀取發送描述符(內存讀),檢查是否有包被硬件發送出去。
8)如果有的話,則從內存中讀取相應控制結構體(內存讀),釋放數據緩沖區。
可以看出處理一個報文的過程中,需要6次讀取內存(上文(內存讀))。
換句話說要保證在80個時鍾周期處理完一個報文DPDK就必須保證要讀取的數據Cache命中,否則一旦Cache不命中,性能會嚴重下降。
7.3 Cache一致性
當定義的數據結構或者分配了數據緩沖區之后,內存中就有了一個地址和其相對應,然后程序進行讀寫。在讀的過程中,首先是內存加載到Cache,隨后送到處理器內部的寄存器;在寫操作的時候則是從寄存器送到Cache,最后由總線回寫到內存。
這樣會出現兩個問題:
1)數據結構/數據緩沖區對應的Cache Line是否對齊?如果不是的話,即使數據區域小於Cache Line的話也會占用兩個Cache Line;另外假如上一個CacheLine屬於另一個數據結構且被另一個處理器核處理,數據如何同步呢?
答案:結構體對齊__rte_cache_aligned;
Doing this can often make copy operations more efficient, because the compiler can use whatever instructions copy the biggest chunks of memory when performing copies to or from the variables or fields that you have aligned this way.
2)假設數據結構/緩沖區的起始地址是CacheLine對齊的,但是有多個核同時對該內存進行讀寫,如何解決沖突?
答案:DPDK解決方案很簡單,首先避免多個核訪問同一個內存地址或者數據結構。每個核盡量避免與其他核共享數據,從而減少因為錯誤的數據共享導致的Cache一致性開銷。
8. Bihash
9. 數據預取
__builtin_prefetch()
https://www.cnblogs.com/dongzhiquan/p/3694858.html
VPP中大量報文處理的數據處理操作
p4 = vlib_get_buffer(vm, from[4]);
vlib_prefetch_buffer_header (p4, LOAD);
CLIB_PREFETCH (p4->data, CLIB_CACHE_LINE_BYTES, STORE);