作者简介:Harry V,Intel 工程师
文章转载自DPDK与SPDK开源社区
简介
本文简要描述了DPDK Eventdev库,介绍了Eventdev库的功能设计目的,以及怎样通过Eventdev框架更好地实现和优化应用。
Eventdev库允许DPDK应用使用事件驱动的工作运行模式。事件驱动中,系统要完成的工作用一种被分割的单元来表示,这种被分割的单元即为事件。每一个事件代表一个单独的任务或个体,这种任务或个体可以使用Eventdev库达到被调度的目的。换言之,即可以将一个事件看作一个单独的网络报文;这种用法在DPDK的事件驱动编程中非常常见。
类似于DPDK的ethdev和cryptodev库,Eventdev库使用了一种通过后端执行调度的模型,这种后端驱动(被称作轮训模式驱动,简称PMDs)执行真正的调度工作。设计良好的通用型应用可以使用多种轮训模式的驱动达到运行目的。
使用轮训模式驱动的原因在于其通常可以为系统执行的任务提供更好的负载均衡功能。下图是一个运作的Eventdev库:
负载分割
为了用事件驱动的方式来设计一个应用,报文处理的负载必须被分为多个阶段。在整个任务输入到输出的处理过程中,每个阶段均应是一个逻辑完整的步骤。这种负载分割的优点在于每个逻辑阶段可以被不同的CPU核执行,可以使事件通过负载均衡被分配到不同的CPU核上,同时充分利用CPU的运算能力。
每个逻辑阶段都在Eventdev中有所表达。这些逻辑阶段允许DPDK Eventdev库对事件执行调度,在每个阶段后执行负载均衡。因此,工作负载被处理后,可以用于调整任务中CPU核的数量,这解决了大规模负载引起的单核事件处理性能的瓶颈问题。
从性能和cache-locality的角度而言,Eventdev使用的管道策略似乎导致了更多开销,但是在系统中均衡地使用CPU通常能更好地利用CPU的预测能力,同时提升性能。没有使用Eventdev的系统中总会出现每个CPU的不均衡使用,这会导致整体的系统性能变成局部最优,因为部分CPU处于闲置状态,其他CPU则处于过度使用状态。
Eventdev API 构成
Eventdev本身是一个DPDK库,以共有API的形式暴露给应用。应用能够选择是否需要使用Eventdev库的功能。如果需要,应用需引入使用Eventdev架构的API。应用不需要Eventdev也可以,因为DPDK不强制其使用,开发者可以在更加适合的使用场景下选择Eventdev。
Eventdev API 分为以下几个部分:
— 事件设备 (Event Device)
— 事件队列 (Event Queue)
— 事件端口 (Event Port)
— 端口到队列的链接 (Port to Queue Links)
事件设备
事件设备是一个可以执行事件调度的设备。类似于DPDK的以太网设备,每个设备均拥有唯一的ID值来标识自己。
事件队列
事件队列是管道中的一个逻辑阶段。应用中的典型用法为:创建少量的队列,每个队列都代表报文处理中的一个阶段(详情见《负载分割》部分)。
事件端口
事件端口用于事件设备与CPU逻辑核之间的通信。每个事件端口均被一个CPU逻辑核使用。因此,如果一个含8个CPU逻辑核的应用使用Eventdev,将会有8个事件端口。
端口到队列的链接
为了使事件从一个队列到达一个端口,该队列和端口之间需要被链接。当应用从队列链接至端口,即意味着正在通知Eventdev从该队列到端口执行事件调度。
Eventdev 示例应用
DPDK的每个库通常都会有相应的示例应用,Eventdev也不例外。Eventdev的示例应用展示了DPDK应用基于事件和管道处理模式的运行方式。
下文中展示的Eventdev示例应用是使用了Eventdev库和基于软件的轮训模式驱动来执行调度的通用应用。基于此,该示例应用被命名为eventdev_pipeline_sw_pmd(已更名为eventdev_pipeline),包含于DPDK的源码之中,可用于17.08-rc1之后的版本。
为了运行该示例应用,必须选择一个轮训模式驱动来执行事件调度。本教程将使用Eventdev SW PMD——该驱动的调度功能由纯软件实现。该软件的轮训模式驱动可以在任何支持DPDK的CPU或平台上运行。
示例应用的基本用法
Eventdev的示例应用是可配置的,允许用户指定许多参数来决定Eventdev需要模拟配置的类型。在该案例中,配置的类型指队列的数量和端口的使用情况(参考上文《Eventdev API构成》了解关于队列和端口的细节)。
示例应用使用了一种标准方法来设计基于事件的DPDK应用。该示例应用需要制定使用的轮训模式驱动类型,所以示例应用启动命令行的部分需要指定驱动类型:
./build/eventdev_pipeline_sw_pmd —vdev event_sw0
其中,某些特定的CPU核将履行特定的目的,剩下的CPU核则作为worker cores。这些核担任的主要角色包括:
— RX Core:从网卡收包
— Sched Core: 执行调度工作
— TX Core: 向网卡发包
— Worker Core:执行示例程序中的具体工作
上述的每个角色均须分派给特定的CPU核来执行其功能。若一个特定的角色是某核上运行的唯一任务,该核将只会执行这个角色的工作。若多个角色(如RX和Scheduler)被赋予一个特定的核,该核将同时执行所有角色的工作。从性能角度而言,理想的状态是使用一个核专门用于调度。若并未为上述角色分配CPU核,应用将会启动失败, 失败信息类似于下:
Core part of pipeline was not assigned any cores. This will stall the pipeline, please check core masks (use -h for details on setting core masks):
1 2 3 4 5 6 7 |
rx: 0 tx: 0 sched: 0 workers: 0 EAL: Error - exiting with code: -1 Cause: Fix core mask |
为了给一个特定的角色分配一组CPU核,可以提供诸如下方所示的参数。参数为CPU核掩码,每个角色均有自己的CPU核掩码。
1 2 3 4 5 |
-r 0x1 # set RX role coremask to only lcore 1 -t 0x2 # set TX role coremask to only lcore 2 -e 0x4 # set Scheduler coremask to only lcore 3 -w 0xf0 # set worker cores to lcore 5,6,7,8 |
最后,启动示例应用完整的工作命令行为:
./build/eventdev_pipeline_sw_pmd —vdev event_sw0 —
-r 0x1 -t 0x2 -e 0x4 -w 0xf0
输出应如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
EAL: Detected XX cores: EAL: ... various EAL messages Config: ports: 4 workers: 4 packets: 33554432 Queue-prio: 0 qid0 type: atomic Cores available: XX Cores used: 7 Eventdev 0: event_sw Stages: Stage 0, Type Atomic Priority = 128 |
示例应用的进阶使用
示例应用允许为模拟设备配置不同类型的管道。该配置只能在启动应用时使用命令行参数来指定,因为应用并未提供一个交互模式。启动命令行的参数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Usage: eventdev_demo [options] Options: -n, --packets=N Send N packets (default ~32M), 0 implies no limit -f, --atomic-flows=N Use N random flows from 1 to N (default 16) -s, --num_stages=N Use N atomic stages (default 1) -r, --rx-mask=core mask Run NIC rx on CPUs in core mask -w, --worker-mask=core mask Run worker on CPUs in core mask -t, --tx-mask=core mask Run NIC tx on CPUs in core mask -e --sched-mask=core mask Run scheduler on CPUs in core mask -c --cq-depth=N Worker dequeue ring depth (default 16) -W --work-cycles=N Worker cycles (default 0) -P --queue-priority Enable scheduler queue prioritization -o, --ordered Use ordered scheduling -p, --parallel Use parallel scheduling -q, --quiet Minimize printed output -D, --dump Print detailed statistics before exit |
为了设计一个特殊的管道,通常使用以下参数:
--n0 Removes number of packets limit, the application continues forever
-s4 Set the number of stages in the pipeline, for example 4 stages
-W1000 Set the number of cycles each event will take to process on the CPU
-c32 Set the depth of the worker dequeue
上述参数允许以多种配置形式来运行应用。通过设定应用无限期执行可以进行长期的性能分析,获得更精确的视角。通过设定不同的管道长度,可以使应用模拟特定的工作负载。通过指定每个事件在worker core上消耗的CPU周期数,可以模拟更真实的系统行为。Worker队列的深度可以调节性能,实施理想的负载均衡。这个值应设置得尽量高,从而获得可接受的负载均衡。
使用Ctrl+C可以停止应用,每个worker的统计数据会被打印出来:
1 2 3 4 5 6 7 8 9 |
Port Workload distribution: worker 0 : 13.1 % (23450532 pkts) worker 1 : 12.9 % (23065448 pkts) worker 2 : 12.8 % (22817455 pkts) worker 3 : 12.6 % (22427380 pkts) worker 4 : 12.3 % (22009860 pkts) worker 5 : 12.2 % (21761200 pkts) worker 6 : 12.1 % (21594812 pkts) worker 7 : 12.0 % (21469786 pkts) |
Eventdev驱动的统计功能
应用提供的统计数据在上文中有所展示。若需观察Eventdev驱动的行为,可以使用以下命令行参数,了解所有关于Eventdev驱动的统计信息:
1 2 |
-D Dump statistics of the application on quit |
Eventdev软件驱动的输出信息相当详细,其中提供了许多设备状态的细节。以下是使用下述命令行、运行含两级原子管道和四个worker core的Eventdev软件驱动时产生的输出数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
./build/eventdev_pipeline_sw_pmd --vdev event_sw0 -- \ -r 0x1 -t 0x2 -e 0x4 -w 0xf0 -n0 -c32 -s2 -n100000000 -D EventDev: ports 6, qids 3 rx 300002372 drop 0 tx 300001657 sched calls: 1574543 sched cq/qid call: 2356411 sched no IQ enq: 96017 sched no CQ enq: 9938 inflight 1152, credits: 2944 Port 0 rx 46196017 drop 0 tx 46196049 inflight 32 Max New: 4096 Avg cycles PP: 193 Credits: 32 Receive burst distribution: 0:97% 1-4:0.27% 5-8:0.26% 9-12:0.31% 13-16:1.67% rx ring used: 0 free: 4096 cq ring used: 32 free: 0 Port 1 rx 44454196 drop 0 tx 44454228 inflight 32 Max New: 4096 Avg cycles PP: 192 Credits: 32 Receive burst distribution: 0:98% 1-4:0.27% 5-8:0.26% 9-12:0.30% 13-16:1.55% rx ring used: 0 free: 4096 cq ring used: 32 free: 0 Port 2 rx 44049832 drop 0 tx 44049864 inflight 32 Max New: 4096 Avg cycles PP: 194 Credits: 32 Receive burst distribution: 0:98% 1-4:0.27% 5-8:0.26% 9-12:0.30% 13-16:1.53% rx ring used: 0 free: 4096 cq ring used: 32 free: 0 Port 3 rx 65301351 drop 0 tx 65301495 inflight 144 Max New: 4096 Avg cycles PP: 192 Credits: 32 Receive burst distribution: 0:96% 1-4:0.24% 5-8:0.27% 9-12:0.30% 13-16:3.39% rx ring used: 102 free: 3994 cq ring used: 32 free: 0 Port 4 (SingleCons) rx 0 drop 0 tx 100000021 inflight 0 Max New: 4096 Avg cycles PP: 0 Credits: 37 Receive burst distribution: 0:81% 1-4:1.19% 5-8:0.63% 9-12:0.75% 13-16:16.87% rx ring used: 0 free: 4096 cq ring used: 16 free: 112 Port 5 rx 100000976 drop 0 tx 0 inflight 0 Max New: 1200 Avg cycles PP: 0 Credits: 16 Receive burst distribution: 0:-nan% rx ring used: 0 free: 4096 cq ring used: 0 free: 8 Queue 0 (Atomic) rx 100000976 drop 0 tx 100000976 Per Port Stats: Port 0: Pkts: 25537160 Flows: 13 Port 1: Pkts: 24761228 Flows: 11 Port 2: Pkts: 24652123 Flows: 25 Port 3: Pkts: 25050465 Flows: 62 Port 4: Pkts: 0 Flows: 0 Port 5: Pkts: 0 Flows: 0 -- iqs empty -- Queue 1 (Atomic) rx 100000864 drop 0 tx 100000660 Per Port Stats: Port 0: Pkts: 20658889 Flows: 21 Port 1: Pkts: 19693000 Flows: 26 Port 2: Pkts: 19397741 Flows: 10 Port 3: Pkts: 40251030 Flows: 65 Port 4: Pkts: 0 Flows: 0 Port 5: Pkts: 0 Flows: 0 iq 2: Used 204 Free 307 Queue 2 (Directed) rx 100000532 drop 0 tx 100000021 Per Port Stats: Port 0: Pkts: 0 Flows: 0 Port 1: Pkts: 0 Flows: 0 Port 2: Pkts: 0 Flows: 0 Port 3: Pkts: 0 Flows: 0 Port 4: Pkts: 0 Flows: 0 Port 5: Pkts: 0 Flows: 0 iq 2: Used 511 Free 0 |
如上所示,输出信息相当详细,其中提供了诸多关于驱动状态的细节。输出信息主要分为三个部分——设备、端口和队列。关于设备的部分显示了设备的统计数据,如收包总数、发包总数和Eventdev中当前inflight的事件数量。端口部分包含一些可用于观测每个端口状态的统计信息。其中的趣点是inflight计数和接收突发分布(receive burst distribution),从中可以看出调度器的执行效率。最后,队列部分展示了每个队列状态的统计信息、具体的事件数量和正在被处理的流数量。
总结
本文介绍了Eventdev库,阐释了相关概念(设备、端口和队列),同时通过示例应用的设计和具体运行方式展示了eventdev库的应用方法。例如,进阶用法中陈述了配置管道来模拟特定应用负载的方法,以及如何转出详细的统计信息至终端以进行深入研究。希望本文对你有所帮助,任何问题均可在下方留言评论。