轉發:https://software.intel.com/en-us/articles/dpdk-performance-optimization-guidelines-white-paper
轉發:http://zhaozhanxu.com/2016/08/09/DPDK/2016-08-09-dpdk-optimization/
注:本文是參照了一些其他文章,原文地址點擊這里。
首先根據這篇文章進行了性能瓶頸的分析
策略與方法
首先根據木桶原理,首先要找到最弱的地方,怎么找往上看↑。
想能優化需要考慮如下:
- 優化BIOS設置
- 有效的分配NUMA資源
- 優化Linux配置
- 運行l3-fwd程序驗證以上的配置,和公布的性能對比
- 運行micro-benchmarks選出最佳的更性能組件(比如bulk enqueue/bulk dequeue相對於single enqueue/single dequeue)
- 從dpdk的例子中找一個接近於你想要寫的程序,按照上面的默認配置來(比如TX buffers資源比RX多)
- 適配和更新應用程序,使用正確的優化標志編譯
- 配置你選擇的應用程序,以便有一個比較的基礎
- 運行優化過的命令行參數
- 怎樣為架構匹配更好的應用和算法?運行分析找到內存綁定?I/O綁定?CPU綁定?
- 應用相應的解決方案,軟件內存預取,IO的阻塞模式,CPU綁定超線程或者不
- 重新運行分析,找出是前端pipeline影響還是后端pipeline影響
- 應用正確的解決方案。編寫更加有效的代碼-分支預測(likely),循環展開,編譯優化等等
- 沒有得到期望的性能,回到上面運行優化過的命令行參數,再來一次
- 記錄容易記住的方法,分享到dpdk官網
優化BIOS設置
NUMA | ENABLED |
---|---|
Enhanced Intel® SpeedStep® technology | DISABLED |
Processor C3 | DISABLED |
Processor C6 | DISABLED |
Hyper-Threading | ENABLED |
Intel® Virtualization Technology for Directed I/O | DISABLED |
MLC Streamer | ENABLED |
MLC Spatial Prefetcher | ENABLED |
DCU Data Prefetcher | ENABLED |
DCU Instruction Prefetcher | ENABLED |
CPU Power and Performance Policy | Performance |
Memory Power Optimization | Performance Optimized |
Memory RAS and Performance Configuration -> NUMA Optimized | ENABLED |
請注意,如果要使用DPDK電源管理功能,必須啟用EnhancedIntel®SpeedStep®技術。 此外,應啟用C3和C6。
平台優化
平台優化包括配置內存和I/O(NIC卡)以利用affinity實現更低的延遲。
NUMA & Memory Controller
舉個多socket插座的例子, 對於在CPU0上運行的線程,socket0訪問local memory延遲較低,通過QPI(Intel® QuickPath Interconnect)訪問remote memory延遲較高,盡量避免。
問題:
在BIOS中NUMA設置為DISABLED會發生神馬?內存控制器通過socket交叉訪問。例如,如下所示,CPU0正在讀取256個字節(4個高速緩存行)。 由於BIOS NUMA設置為DISABLED狀態,因為內存控制器交叉存取在256個字節之內的訪問,從本地存儲器讀取128字節,從遠程存儲器讀取128字節。遠程內存訪問最終跨越QPI鏈路。這樣做的影響是訪問遠程內存的訪問時間增加,從而導致性能降低。
解決方案:
如下圖所示,BIOS設置NUMA = Enabled,所有訪問都進入相同的socket(local)內存,沒有QPI交叉。存儲器訪問的延遲較,低性能提高。
PCIe* Layout and IOU Affinity
Linux優化
使用isolcpus減少上下文切換
為了減少上下文切換的可能性,需要提示內核,禁止將其他用戶空間任務調度到DPDK應用線程所在核。isolcpus Linux內核參數用於此目的。
例如,如果DPDK應用程序要在邏輯核心1,2和3上運行,則應將以下內容添加到內核參數列表中:
isolcpus = 1,2,3
注意:即使使用isolcpus提示,調度程序仍可以在隔離的核心上調度內核線程。請注意,isolcpus需要重新啟動。
適配和更新應用程序
現在已經將相關示例應用程序標識為構建最終產品的起點,以下是要回答的下一組問題。
如何配置應用程序以獲得最佳性能?
- 每個端口可以配置多少個隊列?
- Tx資源可以分配為與Rx資源相同的大小嗎?
- 閾值的最佳設置是什么?
建議:好消息是,示例應用程序不僅具有優化的代碼流,而且優化的參數設置為默認值。建議在Tx和Rx的資源之間使用類似的比例。以下是Intel® Ethernet Controller 10 Gigabit 82599的參考和建議。對於其他NIC控制器,請參閱相應的數據手冊。
每個端口可以配置多少個隊列?
有關此主題的詳細測試設置和配置,請參閱白皮書評估軟件路由器的服務器網卡的適用性。
下面的圖(從上面的白皮書)指示每個端口不使用多於2到4個隊列,因為性能隨着更高數量的隊列而降低。
對於最佳情況,建議每個端口使用1個隊列。 如果需要更多,可以考慮每個端口2個隊列,但不能超過2個。
Tx資源可以分配為與Rx資源相同的大小嗎?
為Tx和Rx分配相等大小的資源是一種自然的趨勢。 然而,請注意,http://dpdk.org/browse/dpdk/tree/examples/l3fwd/main.c顯示,Tx ring描述符的數量的最佳默認大小是512,而Rx ring描述符是128.因此,Tx ring描述符的數量是Rx ring描述符的4倍。
建議選擇Tx ring描述符是Rx ring描述符的4倍,而不是讓它們具有相等的大小。
## 閾值的最佳設置是什么?
例如,http://dpdk.org/browse/dpdk/tree/app/test/test_pmd_perf.c具有以下針對Intel Ethernet Controller 10 Gigabit 82599的優化默認參數。
更詳細的說明,請參閱Intel Ethernet Controller 10 Gigabit 82599 data sheet。
Rx_Free_Thresh - 快速總結和關鍵點:PCIe處理(更新硬件寄存器)的成本可以通過處理批量的數據包(在更新硬件寄存器之前)攤銷。
Rx_Free_Thresh - 詳細:如下所示,由硬件接收的報文使用報文描述符的循環緩沖器來完成。在循環緩沖器中可以有多達64K-8個描述符。硬件維護卷影副本,包括已完成但尚未存儲在內存中的那些描述符。
RDH(Receive Descriptor Head register)表示正在進行的描述符。
RDT(Receive Descriptor Tail register)表示超出硬件可以處理的最后一個描述符的位置。 這是軟件寫入第一個新描述符的位置。
在運行時,軟件處理描述符,並在描述符完成后,RDT+1。 然而,在軟件處理每個分組之后更新RDT具有成本,因為其增加了PCIe操作。
Rx_free_thresh表示DPDK軟件在將其發送回硬件之前將保持的空閑描述符的最大數量。 因此,通過在更新RDT之前批量處理報文,我們可以減少該操作的PCIe成本。
使用您的配置的rte_eth_rx_queue_setup()函數中的參數進行微調
1
2
3
|
ret = rte_eth_rx_queue_setup(portid,
0, rmnb_rxd,
socketid, &rx_conf,
mbufpool[socketid]);
|
使用正確的優化參數編譯
應用相應的解決方案:軟件預取的內存,I/O阻塞,超線程或不超線程為CPU綁定的應用程序。
內存的軟件預取有助於降低存儲器延遲。
PREFETCHW:預期寫數據時預取到高速緩存:CPU Haswell的新指令,幫助優化降低內存延遲並改善網絡堆棧。
PREFETCHWT1:意圖寫入的預取提示T1(L1高速緩存):CPU Haswell的新指令,意圖寫入提示(使數據通過所有權請求進入“獨占”狀態),將數據提取到指定的高速緩存層級中的位置和位置提示。
有關這些說明的更多信息,請參閱Intel® 64 and IA-32 Architectures Developer’s Manual。
使用優化的命令行參數運行
優化應用程序命令行選項,提高affinity、locality和並發性。
coremask參數
coremask參數指定lcore運行DPDK應用程序。更高的性能,降低處理器間通信成本是關鍵。這樣的溝通的核心是物理的鄰居,應該選擇coremask。
問題︰ DPDK的命令行coremask參數指定了lcore 0和lcore 1兩個相鄰芯。請注意,這些lcore號分別映射到指定的NUMA sockets上。不同的平台是不一樣的,這個平台上可能lcore 0和1是鄰居,有些平台可能不是。
如下圖所示,在單socket,lcore 0和lcore 4是相同的物理core(core 0)的兄弟姐妹。所以lcore 0和 lcore 4之間的通信成本低於lcore 0和lcore 1之間的通信成本。
解決方案:不同的平台布局的時候需要注意使用不同的方案。
查看CPU布局的工具
通過tools目錄的./cpu_layout.py能找出socket ID,物理core ID,lcore ID。根據這些信息來確定coremask參數怎么確定。
dual-socket機器我們看到的CPU布局如下:
物理core為[0, 1, 2, 3, 4, 8, 9, 10, 11, 16, 17, 18, 19, 20, 24, 25, 26, 27]
注意物理core 5, 6, 7, 12, 13, 14, 15, 21, 22, 23不在上面的范圍中,這表明物理核心不一定是連續的。
怎么從cpu布局中找到哪些lcores是超線程的?
在下圖中,lcore 1和lcore 37是socket 0的超線程,將通信任務分配給lcore 1和lcore 37比分配給其他的lcore有更低的消耗和更高的性能。
core 0留給Linux,別給DPDK用
DPDK初始化中,master core默認使用的是lcore 0。如下圖所示
DPDK應用程序盡量不使用lcore 0,因為Linux 把lcore 0作為master core。避免使用l3fwd – c 0x1…,因為這會使用lcore 0。相反,應用程序可以使用lcore 1,命令行為l3fwd — — c 0x2……。
實際使用OVS-DPDK的情況下,控制平面線程綁定lcore 0,負責響應用戶或SDN控制器發出的控制平面命令。所以,DPDK應用程序不應使用lcore 0,設置掩碼的時候不激活lcore 0。
正確使用channel參數
要確保正確使用channel參數。例如,使用channel參數n = 3表示3通道內存系統。
DPDK Micro-benchmarks and auto-tests
auto-tests用於功能驗證。以下是幾個micro-benchmarks性能評價的例子。
怎么測試兩個core的cache line往返消耗的時間
http://dpdk.org/browse/dpdk/tree/app/test/test_distributor_perf.c中的函數Time_cache_line_switch()就是用來測試兩個core的cache line往返所需要的cpu cycles。
怎么測試報文處理時間
http://dpdk.org/browse/dpdk/tree/app/test/test_distributor_perf.c中的函數Perf_test()一次發送32個報文,最后工作線程記錄收到最后一個報文的時間,然后計算出每個報文消耗的時間。
怎么檢測single producer/single consumer(sp/sc)和multi-producer/multi-consumer(mp/mc)之間的性能差異
運行/app/test中的ring_perf_auto_test,會輸出cpu cycles,來對比不同bulk下sp/sc和mp/mc的差別。
關鍵點:高bulk下,sp/sc有更高的性能。
注意,默認ring_perf_auto_test貫穿塊大小8-32的性能測試,但是如果想自定義,需要修改數組bulk_size[]。下圖輸出了1、2、4、8、16、32的塊大小。
2-Socket System – Huge Page Size = 2 Meg
hash_perf_autotest為每個測試項運行1000000次迭代,改變以下的參數並且為每個組合總結出每次操作所用的時鍾滴答(Ticks/Op)
Hash Function | Operation | Key Size (bytes) | Entries | Entries per Bucket |
---|---|---|---|---|
a) Jhash, b) Rte_hash_CRC |
a) Add On Empty, b) Add Update, c) Lookup |
a) 16, b) 32, c) 48, d) 64 |
a) 1024, b) 1048576 |
a) 1, b) 2, c) 4, d) 8, e) 16 |
附錄有詳細的測試輸出和命令,您可以使用來評估您的平台的性能。摘要結果制成表格下, 圖︰
Sl No. | Focus Area to Improve | Use These Micro-Benchmarks and Auto-Tests |
---|---|---|
1 | Ring For Inter-Core Communication | 單獨enqueue/dequeue和批量enqueue/dequeue之間的性能對比 分別對比不同的超線程、cores、sockets上的兩個core之間的批量enqueue/dequeue 空ring上dequeue的性能test_ring_perf.c Single producer, single consumer – 1 Object, 2 Objects, MAX_BULK Objects – enqueue/dequeue Multi-producer, multi-consumer – 1 Object, 2 Objects, MAX BULK Objects – enqueue/dequeue 使用test_ring.c驗證以上幾項 test_pmd_ring.c分驗證Tx Burst和Rx Burst |
2 | Memcopy | 使用test_memcpy_perf.c分別驗證以下幾項: Cache to cache Cache to memory Memory to memory Memory to cache |
3 | Mempool | 使用test_mempool.c驗證以下幾項的n_get_bulk 和n_put_bulk 1 core, 2 cores, max cores with cache objects 1 core, 2 cores, max cores without cache objects |
4 | Hash | 使用test_hash_perf.c驗證Rte_jhash 和rte_hash_crc 的以下幾項Add, Lookup, Update |
5 | ACL Lookup | test_acl.c |
6 | LPM | test_lpm.c |
7 | Packet Distribution | test_distributor_perf.c |
8 | NIC I/O Benchmark | Benchmarks Network I/O Pipe - NIC h/w + PMD Measure Tx Only Measure Rx Only Measure Tx & Rx |
9 | NIC I/O + Increased CPU processing | Increased CPU processing – NIC h/w + PMD + hash/lpm Examples/l3fwd |
10 | Atomic Operations/ Lock-rd/wr | test_atomic.c和test_rwlock.c |
11 | SpinLock | 使用test_spinlock.c驗證以下兩項: 申請全局鎖,做些操作,釋放全局鎖 申請per-lcore鎖,做些操作,釋放per-core鎖 |
12 | Software Prefetch | test_prefetch.c用法為:rte_table_hash_ext.c |
13 | Reorder and Seq. Window | test_reorder.c |
14 | ip_pipeline | 用packet framework創建一個pipeline,test_table.c ACL Using Packet Framework,test_table_acl.c |
15 | Reentrancy | test_func_reentrancy.c |
16 | mbuf | test_mbuf.c |
17 | memzone | test_memzone.c |
18 | Ivshmem | test_ivshmem.c |
19 | Virtual PMD | virtual_pmd.c |
20 | QoS | test_meter.c,test_red.c,test_sched.c |
21 | Link Bonding | test_link_bonding.c |
22 | Kni | 使用test_kni.c驗證以下幾項: 傳輸、從kernel接收、kernel請求 |
23 | Malloc | test_malloc.c |
24 | Debug | test_debug.c |
25 | Timer | test_cycles.c |
26 | Alarm | test_alarm.c |
編譯優化
可以參考圖書Pyster - Compiler Design and construction
性能優化和弱有序注意事項
背景︰Linux內核的同步原語包含所需的內存屏障如下所示(單處理器和多處理器的版本)
Smp_mb() | 內存屏障 |
---|---|
Smp_rmb() | 讀內存屏障 |
Smp_wmb() | 寫內存屏障 |
Smp_read_barrier_depends() | 強制后續的操作是依賴與之前的操作,保證有序性 |
Mmiowb() | 全局spinlocks保障有序的MMIO寫操作 |
使用標准同步原語(spinlocks, semaphores, read copy update)的代碼不必使用內存屏障,因為這些標准的同步原語已經使用了內存屏障。
挑戰:如果你寫的代碼沒有使用標准的同步原語,而又想優化,可以使用內存屏障。
考慮:X86提供了process ordering的內存模型,他是弱一致性的,可以任意打亂順序,除非有內存屏障限制。
smp_mp(), smp_rmb(), smp_wmb()原語禁止編譯器優化,避免內存重新排序優化跨過內存屏障的影響。
一些SSE指令是弱有序的(clflush and non-temporal move instructions),帶有SSE指令的CPU mfence類似smp_mb(),lfence類似smp_rmb(),sfence類似smp_wmb()。
附錄
Pmd_perf_autotest
使用/app/test/pmd_perf_autotest
評估你的平台性能
關鍵點:每個報文RX+TX的開銷是54 cycles,4端口4內存通道
只查看RX的開銷,運行程序pmd_perf_autotest
之前先運行set_rxtx_anchor rxonly
,同樣只查看TX的開銷,運行set_rxtx_anchor txonly
。
Packet Size = 64B # of channels n= 4
of cycles per packet | TX+RX Cost | TX only Cost | Rx only Cost |
---|---|---|---|
With 4 ports | 54 cycles | 21 cycles | 31 cycles |
下圖是TX和RX的數據
Hash Table Performance Test Results
運行/app/test/hash_perf_autotest
評估系統性能。
Memcpy_perf_autotest Test Results
運行/app/test/memcpy_perf_autotest
評估平台系統性能,分別有32位對齊和未對齊的情況
Mempool_perf_autotest Test Results
Core Configuration | Cache Object | Bulk Get Size | Bulk Put Size | of Kept Objects |
---|---|---|---|---|
a) 1 Core b) 2 Cores c) Max. Cores |
a) with cache object b) without cache object |
a) 1 b) 4 c) 32 |
a) 1 b) 4 c) 32 |
a) 32 b) 128 |
運行/app/test/mempool_perf_autotest
評估平台系統性能
## Timer_perf_autotest Test Results
of Timers Configuration | Operations Timed |
---|---|
a) 0 Timer b) 100 Timers c) 1000 Timers d) 10,000 Timers e) 100,000 Timers f) 1,000,000 Timers |
- Appending - Callback - Resetting |
運行/app/test/timer_perf_autotest
評估平台系統性能