DPDK优化

2019年6月13日 0 作者 oceansw

策略与方法


首先根据木桶原理,首先要找到最弱的地方,怎么找往上看↑。

想能优化需要考虑如下:

  • 优化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延迟较高,尽量避免。

avatar

问题:
在BIOS中NUMA设置为DISABLED会发生神马?内存控制器通过socket交叉访问。例如,如下所示,CPU0正在读取256个字节(4个高速缓存行)。 由于BIOS NUMA设置为DISABLED状态,因为内存控制器交叉存取在256个字节之内的访问,从本地存储器读取128字节,从远程存储器读取128字节。远程内存访问最终跨越QPI链路。这样做的影响是访问远程内存的访问时间增加,从而导致性能降低。

avatar

解决方案:
如下图所示,BIOS设置NUMA = Enabled,所有访问都进入相同的socket(local)内存,没有QPI交叉。存储器访问的延迟较,低性能提高。

avatar

PCIe* Layout and IOU Affinity

avatar

avatar

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个。

avatar

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倍。

avatar

建议选择Tx ring描述符是Rx ring描述符的4倍,而不是让它们具有相等的大小。

avatar

## 阈值的最佳设置是什么?

例如,http://dpdk.org/browse/dpdk/tree/app/test/test_pmd_perf.c具有以下针对Intel Ethernet Controller 10 Gigabit 82599的优化默认参数。

avatar

更详细的说明,请参阅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)表示超出硬件可以处理的最后一个描述符的位置。 这是软件写入第一个新描述符的位置。

avatar

在运行时,软件处理描述符,并在描述符完成后,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之间的通信成本。

avatar

解决方案:不同的平台布局的时候需要注意使用不同的方案。

查看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有更低的消耗和更高的性能。

avatar

core 0留给Linux,别给DPDK用

DPDK初始化中,master core默认使用的是lcore 0。如下图所示

avatar

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通道内存系统。

avatar

avatar

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个报文,最后工作线程记录收到最后一个报文的时间,然后计算出每个报文消耗的时间。

avatar

怎么检测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

avatar

avatar

avatar

avatar

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

附录有详细的测试输出和命令,您可以使用来评估您的平台的性能。摘要结果制成表格下, 图︰

avatar

avatar

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_bulkn_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_jhashrte_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.ctest_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内存通道

avatar

只查看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的数据

avatar

avatar

avatar

Hash Table Performance Test Results

运行/app/test/hash_perf_autotest评估系统性能。

avatar

avatar

avatar

avatar

avatar

Memcpy_perf_autotest Test Results

运行/app/test/memcpy_perf_autotest评估平台系统性能,分别有32位对齐和未对齐的情况

avatar

avatar

avatar

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评估平台系统性能

avatar

avatar

## 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评估平台系统性能

avatar

avatar