背景
在SPDK中,每个CPU内核都对应于reactor,并且创建的线程在reactor中运行。
figure1corereactor-thread对应关系
默认情况下,reactor始终以轮询模式运行,以提供最高效的处理。 但是,在reactor空闲时,总是进行轮询,因此会浪费资源。 另外,另一个reactor可能正在执行超出其自身负载的大量工作。 将部分空闲reactor线程移动到空闲reactor将大大提高效率。 或者,如果空闲的reactor使用中断模式,则可以大大减少资源的损失。 这产生了spdk的scheduler模块。
通常,在空闲状态下,调度器可以将reactor切换到“中断模式”。 在Linux上,这是使用epoll实现的,但可能会降低CPU利用率,降低事件发生时的响应速度。 计划程序大大提高了重量轻或变化大的工作负载的效率。
计划器动态旨在提高能效和CPU利用率,使您可以更好地动态管理reactor上的线程,特别是在工作负载显示出随着时间的推移变化很大的情况下。
SPDK_TOP
在介绍scheduler之前,我将向您介绍spdk验证cpu利用率的工具spdk_top。
SPDK _ top APP应用程序类似于标准top,通过spdk轻量级线程和轮询实时反馈CPU核心的使用情况。 spdk _ top APP应用程序使用RPC命令调用收集性能指标并将其显示在报告中。 这使您可以分析和确定代码是否有效运行,从而调整实现并从spdk中获取更多信息。
为什么经典的top实用程序不适用于SPDK? SPDK设计为轮询模式,并分配给在SPDK APP应用程序的每个CPU核心上执行的reactor线程,以调度spdk轻量级线程和轮询。 因此,标准Linux top实用程序对分析轮询模式(如SPDK )的APP应用程序的CPU利用率没有帮助。 这是为了报告分配的CPU资源只有100%被使用。 spdk_top实用程序是为了分析和报告用于执行实际工作和仅轮询工作的CPU周期而开发的。 此实用程序依赖于添加到轮询的工具来跟踪工作和轮询的进行时间。 spdk_top实用程序从轮询中获取细粒度指标,并分析和报告每个轮询、线程和核心指标。
启动spdk _ top APP应用程序(只有在spdk上启动了目标时才能执行) :
$SPDKDIR/build/无言的花瓣/spdk_top
基本操作
spdk_top窗口底部的菜单提供了许多用于更改显示数据的选项。 每个菜单项都有一个与方括号相关联的键。
Switch tab [1-3]-用于选择线程/轮询/核心选项卡。
Sorting [s]-用于按列对排序弹出窗口中显示的数据进行排序。
Total / Interval [t] -将所有选项卡上的显示值更改为total time (在spdk APP应用程序启动后测量)或Interval time (在上次刷新后测量)。
选择线程后,按Enter可查看详细信息。 然后,可以按 ESC 键关闭弹出窗口。
33558 www.Sina.com /提供详细的帮助信息
help [h]
Scheduler dynamic
一
Core 状态表述如果当前core中没有活动线程,请将相应的reactor切换到中断模式。 (如果当前酷睿具有活动线程,则切换到pol
ling mode);2. 当线程的busy时间间隔除以当前线程的一个周期的百分比小于SCHEDULER_LOAD_LIMIT时,当前线程状态为idle;
3. 当线程的busy时间间隔除以当前线程的一个周期的百分比大于SCHEDULER_LOAD_LIMIT时,当前线程状态为busy;
二
线程调度原理
1. 当创建线程的活跃时间百分比小于SCHEDULER_LOAD_LIMIT(代码中设定为20)时,该线程为idle状态,此时该线程将移动到main core上。
Figure 2 balance 原理
示例:
启动一个target
test/event/scheduler/scheduler和它的rpc plugin是测试例程,在此用来评估观测scheduler行为,实际应用中用实际需要启动的target即可。
$SPDKDIR/test/event/scheduler/scheduler -m 0xF -p 2 --wait-for-rpc &
scripts/rpc.py framework_set_scheduler dynamic
scripts/rpc.py framework_start_init
这时,启动spdk_top,可以观察到当前只有app_thread(即启动的target)一个线程,并且在我们指定的main core(core2)上。
接着创建4个idle线程(活跃时间占比分为0)
rpc_cmd --plugin scheduler_plugin scheduler_thread_create -n idle0 -m 0x1 -a 0
rpc_cmd --plugin scheduler_plugin scheduler_thread_create -n idle1 -m 0x2 -a 0
rpc_cmd --plugin scheduler_plugin scheduler_thread_create -n idle2 -m 0x4 -a 0
rpc_cmd --plugin scheduler_plugin scheduler_thread_create -n idle3 -m 0x8 -a 0
之后,我们可以观察到,虽然创建时指定了这些线程绑定不同的core,但最终仍通过scheduler将这些线程全部分配在core2(main core)上:
当main core上不存在活动线程时,该 CPU 内核的频率将随着负载的降低而降低。与其他reactor对应的所有 CPU 内核都保持在最大频率。
因此,当前的reactor并没有活跃线程,所以此时main core处于低功率状态:
2.当前线程活跃占比大于SCHEDULER_LOAD_LIMIT时,当前线程的状态为busy,scheduler将会为当前线程找一个最合适的reactor,然后将其移动过去。Main core应尽量处于空闲状态,只有当线程的执行时间超过所有线程空闲时间的总和时,main core才能包含活动线程。
1)scheduler调度的目标reactor只能在target绑定的core(即启动target时设置的cpumask对应的core)上的reactor中进行选择。
2)调度的目标reactor必须有足够的空间能放下该线程。
3)调度的目标reactor应是当前polling mode的reactor中最空闲的(先排除interrupt mode的reactor,除非没有合适的才会选用)。
4)当符合以上条件时,如果 main core所对应的reactor可以容纳下当前线程,则应将当前线程移动到main core上。
示例:
创建一个活跃占比为50的线程,根据调度原则此时该线程应分配在core0上:
rpc_cmd --plugin scheduler_plugin scheduler_thread_create -n busy0 -a 50
我们再创建一个活跃占比为40的线程:
rpc_cmd --plugin scheduler_plugin scheduler_thread_create -n busy1 -a 40
通过spdk_top可以看出,busy1分配在core2(main core)上。
5)当main core容纳不了该线程时,则优先选择合适的core中core id最小的core。
示例:
在之前的基础上再创建一个活跃占比为40的线程:
rpc_cmd --plugin scheduler_plugin scheduler_thread_create -n busy2 -a 40
此时,core0、core1、core3都是空闲状态,core0 id最小,则优先分配在core0上。
6)当前core如果超出限制时,则之外的任一 core 都比当前core要更合适。对于超过限制的内核,将线程放在最不忙的内核上以平衡线程。
限制条件:
a.当前core无线程或单线程
b.没有正在运行的线程,即busy时间为0
c.完成的工作少于限制(SCHEDULER_CORE_LIMIT 95)
示例:
先清除之前所有的线程,然后在core1上创建线程busy0:
rpc_cmd --plugin scheduler_plugin scheduler_thread_create -n busy0 -m 0x2 -a 50
再在core0上创建busy1,活跃时间占比为30:
rpc_cmd --plugin scheduler_plugin scheduler_thread_create -n busy1 -m 0x1 -a 30
发现busy1被调度到了core1上,这是因为core0上并没有活动线程,此时应处于interrupt mode,因此core1比core0更适合去容纳busy1。
7 )如果没有找到更合适的,则不发生移动,该线程仍在当前core上。
3.调度流程图
4. 当reactor没有调度的spdk_threads 时,它会切换到中断模式并停止主动轮询。在足够多的线程变为活动状态后,reactor将切换回轮询模式并再次为其分配线程。
5. 如果cpu支持变频的话,则可根据需要改变main core的频率。
变频规则:
1)如果该线程不在main core上,那么为main core设置默认频率。
示例:
我们先创建一个活跃占比为10 的线程,此时观察spdk_top,发现该线程被分配在main core(core2)上,此时core2的频率为1000Mhz。
之后我们再创建一个活跃占比为50的线程,将被分配在core0上,此时main core(core2)将升至默认频率2300MHz。
2)如果main core上busy时间大于idle时间,那么为main core进行升频操作。
3)其他情况则为main core进行降频操作(main core为idle,且其他core上无线程)。
示例:
将除app_thread之外的其他线程都销毁掉,我们再观察spdk_top,发现main core(core2)的频率降频至1000MHz。
至此scheduler动态平衡的原理及流程规则全部讲完。
结语
Scheduler_dynamic 实现自动化动态平衡资源,是我们最终的目标,这样就可以极大程度上帮我们合理利用资源,提高工作效率的同时也可以在一定程度上延长设备寿命。同时,spdk_top也是一个非常棒的工具,可以让我们真正地看到资源的使用情况,并针对性地做出准确的分析及合理去改变设备的运行状态。目前两者都处于试验状态,并在不断地完善中。
参考资料
SPDK官网文档:
https://spdk.io/doc/spdk_top.html
https://spdk.io/doc/scheduler.html
《SPDK负载均衡初探》Xiaodong, Liu
转载须知
DPDK与SPDK开源社区
公众号文章转载声明
推荐阅读
2021美国峰会系列二 | SPDK与云原生
基于英特尔平台的三星5G核心网高性能UPF 305 Gbps解决方案
SPDK 负载均衡初探
基于DPDK的设备虚拟化框架
点点“赞”和“在看”,给我充点儿电吧~