欢迎来到运维革新

运维革新

SPDK动态负载均衡

时间:2025-11-26 20:26:55 出处:网络安全阅读(143)

背景

在SPDK中,动态每个CPU core将对应一个reactor ,负载创建的均衡线程将在reactor上执行 。

Figure 1 core – reactor - thread 对应关系

默认情况下 ,动态reactor以一种不断轮询的负载模式运行,以实现最有效的均衡处理  。但是动态当reactor空闲时,仍会不断轮询 ,负载这就造成了资源浪费 ,均衡同时,动态可能有别的负载reactor正在运行超过自身负载的大量工作 ,云计算如果将忙碌状态的均衡reactor上的一部分线程移动到空闲的reactor上 ,将大大提高效率 。动态或者此时空闲状态的负载reactor如果使用interrupt模式,则也可大大降低资源的均衡损耗。由此spdk中的scheduler模块应运而生 。

通常在空闲状态下 ,scheduler可以将reactor切换至中断模式(interrupt mode)。在 Linux 上 ,这是使用 epoll 实现的源码库,但会导致 CPU 使用率降低,而且在事件发生时响应速度可能会降低 。Scheduler则大大提高轻量级或大变化工作负载的效率 。

Scheduler dynamic专为节能和提高CPU利用率,尤其是在工作负载表明随着时间的推移发生大的变化的情况下 ,能更好地动态化管理reactor上的线程。

SPDK_TOP

在介绍scheduler之前,我会先讲解一个spdk用来查看cpu使用率的工具——spdk_top 。模板下载

spdk_top 应用程序类似于标准 top,它通过 SPDK 轻量级线程和轮询器提供对 CPU 内核的使用情况进行实时反馈 。spdk_top 应用程序使用 RPC 命令调用来收集性能指标并将它们显示在报告中,这样您就可以分析和确定您的代码是否有效运行 ,以便您可以调整您的实现并从 SPDK 中获得更多 。

为什么经典的top实用程序不适用于 SPDK?SPDK 采用轮询模式设计,通过分配给 SPDK 应用程序的每个 CPU 内核上运行的reactor线程来调度 SPDK 轻量级线程和轮询器 。因此 ,标准 Linux top 实用程序对于分析 SPDK 等轮询模式应用程序的高防服务器 CPU 使用率无效 ,因为它只会报告它们正在使用分配给它们的 CPU 资源的 100%。开发 spdk_top 实用程序是为了分析和报告用于执行实际工作与仅轮询工作的 CPU 周期。该实用程序依赖于添加到轮询器的工具来跟踪他们何时在工作与轮询工作。spdk_top 实用程序从轮询器获取细粒度指标 ,分析并报告每个轮询器  、线程和核心的指标。

启动 spdk_top 应用程序(SPDK中有target启动时才可执行)  :

复制$SPDKDIR/build/bin/spdk_top1. 基本操作spdk_top窗口底部的菜单显示了许多用于更改显示数据的选项。源码下载每个菜单项在方括号中都有一个与之关联的键 。Switch tab [1-3] - 允许选择线程/轮询器/核心选项卡。Sorting [s] - 允许在排序弹出窗口中按列对显示的数据进行排序。Total / Interval [t] - 将所有选项卡中的显示值更改为 Total time(自 SPDK 应用程序启动后测量)或 Interval time(自上次刷新后测量) 。

选中线程后按Enter,会显示详细信息 ,然后可以通过按 ESC 键关闭弹出窗口:

help [h] – 显示更多帮助信息

Scheduler dynamic

一 、Core 状态表述

当前core上没有任何活动的线程时 ,将把对应的reactor切换为interrupt mode(直到当前core上有活跃线程时,再切换为polling mode);当线程的busy时间间隔除以当前线程的免费模板一个周期的百分比小于SCHEDULER_LOAD_LIMIT时,当前线程状态为idle;当线程的busy时间间隔除以当前线程的一个周期的百分比大于SCHEDULER_LOAD_LIMIT时,当前线程状态为busy;

二、线程调度原理

当创建线程的活跃时间百分比小于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 dynamicscripts/rpc.py framework_start_init1.2.3.

这时 ,启动spdk_top,可以观察到当前只有app_thread(即启动的target)一个线程,并且在我们指定的main core(core2)上。

接着创建4个idle线程(活跃时间占比分为0)

复制rpc_cmd —plugin scheduler_plugin scheduler_thread_create -n idle0 -m 0x1 -a 0rpc_cmd —plugin scheduler_plugin scheduler_thread_create -n idle1 -m 0x2 -a 0rpc_cmd —plugin scheduler_plugin scheduler_thread_create -n idle2 -m 0x4 -a 0rpc_cmd —plugin scheduler_plugin scheduler_thread_create -n idle3 -m 0x8 -a 01.2.3.4.

之后,我们可以观察到,虽然创建时指定了这些线程绑定不同的core ,但最终仍通过scheduler将这些线程全部分配在core2(main core)上 :

当main core上不存在活动线程时 ,该 CPU 内核的频率将随着负载的降低而降低。与其他reactor对应的所有 CPU 内核都保持在最大频率  。

因此 ,当前的reactor并没有活跃线程 ,所以此时main core处于低功率状态:

2.当前线程活跃占比大于SCHEDULER_LOAD_LIMIT时 ,当前线程的状态为busy ,scheduler将会为当前线程找一个最合适的reactor,然后将其移动过去 。Main core应尽量处于空闲状态,只有当线程的执行时间超过所有线程空闲时间的总和时 ,main core才能包含活动线程。

cheduler调度的目标reactor只能在target绑定的core(即启动target时设置的cpumask对应的core)上的reactor中进行选择。调度的目标reactor必须有足够的空间能放下该线程。调度的目标reactor应是当前polling mode的reactor中最空闲的(先排除interrupt mode的reactor,除非没有合适的才会选用) 。当符合以上条件时,如果 main core所对应的reactor可以容纳下当前线程,则应将当前线程移动到main core上 。

示例 :创建一个活跃占比为50的线程 ,根据调度原则此时该线程应分配在core0上  :

复制rpc_cmd —plugin scheduler_plugin scheduler_thread_create -n busy0 -a 501.

我们再创建一个活跃占比为40的线程:

复制rpc_cmd —plugin scheduler_plugin scheduler_thread_create -n busy1 -a 401.

通过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 401.

此时,core0、core1、core3都是空闲状态,core0 id最小 ,则优先分配在core0上。

6)当前core如果超出限制时  ,则之外的任一 core 都比当前core要更合适。对于超过限制的内核,将线程放在最不忙的内核上以平衡线程。

限制条件 :

当前core无线程或单线程没有正在运行的线程,即busy时间为0完成的工作少于限制(SCHEDULER_CORE_LIMIT 95)

示例 :先清除之前所有的线程 ,然后在core1上创建线程busy0:

复制rpc_cmd —plugin scheduler_plugin scheduler_thread_create -n busy0 -m 0x2 -a501.2.

再在core0上创建busy1,活跃时间占比为30:

复制rpc_cmd —plugin scheduler_plugin scheduler_thread_create -n busy1 -m 0x1 -a301.2.

发现busy1被调度到了core1上,这是因为core0上并没有活动线程 ,此时应处于interrupt mode ,因此core1比core0更适合去容纳busy1。

7 )如果没有找到更合适的,则不发生移动 ,该线程仍在当前core上。

3.调度流程图

当reactor没有调度的spdk_threads 时 ,它会切换到中断模式并停止主动轮询 。在足够多的线程变为活动状态后,reactor将切换回轮询模式并再次为其分配线程 。

如果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也是一个非常棒的工具  ,可以让我们真正地看到资源的使用情况,并针对性地做出准确的分析及合理去改变设备的运行状态。目前两者都处于试验状态 ,并在不断地完善中 。

分享到:

温馨提示:以上内容和图片整理于网络,仅供参考,希望对您有帮助!如有侵权行为请联系删除!

友情链接: