Linux内核sched_ext BPF调度器实战——AI训练与推理服务混部场景下的自定义调度策略

问题定义:为什么CFS在AI混部场景下失灵

2025年AI基础设施领域有一个令人头疼的现实:训练任务吃满GPU时,同节点上部署的推理服务P99延迟可以飙升3到5倍。根本原因不在GPU侧,而在CPU调度侧。

Linux CFS调度器的设计目标是"公平"而非"可控"。当一个节点上同时存在以下两类任务时:

  • AI训练任务(DataLoader多进程数据预取,核绑定,吞吐优先)
  • LLM推理服务(Token生成,延迟敏感,毫秒级SLO)

CFS会将CPU时间平均分配给所有可运行任务。DataLoader进程的高CPU权重会挤压推理进程的调度延迟,TTFN(Time To First Token)从基准的80ms膨胀到200ms以上。更糟的是,CFS对NUMA拓扑无感知,训练任务频繁跨Socket访问推理任务的内存,QPI总线成为隐形瓶颈。

理想的解决方案需要满足三个条件:可以精确控制任务优先级、可以做NUMA感知放置、不修改内核源码可以热加载。Linux 6.12 引入的sched_ext恰好满足这三个条件。

sched_ext架构总览

sched_ext是Linux 6.12引入的BPF调度器框架,允许用户空间通过编写BPF程序完全替换内核调度器逻辑。它的核心设计哲学是"调度策略即代码"——你不需要重新编译内核,只需编译一个BPF程序加载即可。


┌─────────────────────────────────────────────────────────────────┐
│                     用户空间                                      │
│  scx_rusty  scx_lavd  scx_layered  你的自定义调度器(scx_ai_co)    │
└─────────────────────────────────────────────────────────────────┘
                            │ BPF skeleton
┌─────────────────────────────────────────────────────────────────┐
│                     内核空间                                      │
│  sched_ext core  ── enqueue_task  ── dispatch  ── select_cpu    │
│                   ── task_tick     ── set_weight                │
└─────────────────────────────────────────────────────────────────┘
                            │
                     CPU 0  CPU 1  ...  CPU N

生产环境目前有三个成熟BPF调度器可供参考:

  • scx_rusty:全局vtime调度,简单高效,适合HPC
  • scx_lavd:基于延迟的调度,自带NUMA感知,适合交互式负载
  • scx_layered:分层调度,支持按cgroup分组策略

实战中我们发现,对于AI混部场景,这三个调度器都不能开箱解决问题,需要从源码级定制。

设计混部调度器的核心思路

一个生产可用的AI混部调度器需要实现四个机制:

1. 任务分类与标记

推理服务通过独立Pod运行,cgroup路径统一为 /kubepods/inference/*,训练DataLoader使用 /kubepods/training/*。调度器在enqueue_task时检查cgroup归属:


static bool is_inference_task(struct task_struct *p)
{
    struct cgroup *cg;
    bool result = false;
    
    rcu_read_lock();
    cg = task_css(p, cpu_cgrp_id)-                        
                    
点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部