目录

面向 RISC-V Vector 扩展的并发 LLM 推理 V 状态感知调度

赛道: 高校组 · 赛题: llama.cpp RISC-V Vector 状态调度优化 · 平台: SpacemiT K3(RVV 1.0 真实硬件) · 框架: llama.cpp server


一、作品简介

本作品研究在真实 RVV 1.0 硬件上,llama.cpp 并发 LLM 推理场景中 RISC-V Vector(V)扩展状态的操作系统调度问题。实验平台为 SpacemiT K3 开发板(8× X100 通用核,VLEN 256;8× A100 AI 核,VLEN 1024),工作负载为 llama.cpp server 模式下的 Llama-3.2-1B / 3B(Q4_K_M)并发推理。

作品包含三部分交付:

  1. V 状态开销量化工具链(必做项 1) —— 基于 ftrace sched:sched_switch 静态 tracepoint 的 V 状态保存/恢复代理 tracer(vstate_tracer.py)、VLEN 探测工具(vlen_probe.c)、ggml barrier convoy 插桩(GGML_CONVOY_PROF),以及支持 TTFT / TPS / ITL(inter-token latency)指标与均值/标准差统计的并发基准测试框架(vstate_bench.py)。
  2. 两类调度优化(必做项 2) ——
    • (a)放置纪律:基于 CPU affinity / cgroup cpuset 的核心绑定与多租户核心分区(apply_placement.py / place_surgical.py);
    • (b)scx_vstate V 状态感知调度器:一个自包含的 sched_ext BPF 调度器,可在真实板卡上加载运行,并与应用侧协同设计(ggml 阶段提示 GGML_VSTATE_HINTS + 被动 barrier)。
  3. 真实硬件性能评测(必做项 3) —— 统一基线下 server 模式、2/4/8 并发、≥3 次重复、均值 ± 标准差的完整评测,覆盖模型规模维度与 VLEN 缩放维度。

核心结论(一句话)

在 VLEN 256 下,V 状态保存本身开销很小(约 0.1 MB/s),并不是瓶颈;真正的成本是过度订阅下的 barrier convoy。真正有效的优化是放置纪律(多租户核心分区带来 +52% 总吞吐提升,线程数匹配核心数带来 **+10%**);而把 V 状态切换减少 10 倍的 sched_ext 调度器并不能提升吞吐——因为切换本来就不是成本中心。该结论在 VLEN 256 与 VLEN 1024 下均成立。

关键实测结果

优化 V 状态切换 吞吐 说明
线程数 = 核心数(-t 8,不过度订阅) −92% +10% 消除 dirty-V ↔ dirty-V 自抢占
多租户核心分区(2 实例 × 并发 4) −97% +52% 消除 barrier convoy,最强收益
低负载预留 2 核(-t 6,并发 1) — −3% ITL P95 **−31%**,抖动好 4–15 倍(3 组配对重复验证)
scx_vstate 调度器 + 阶段提示(诚实负结果) −90% −3.6%(vs 原生 CFS) 切换减少 10 倍但非瓶颈,如实报告

特色与创新

  • 问题重构(convoy reframing):通过直接测量证明该平台上 V 状态保存不是瓶颈,真正问题是过度订阅下的 barrier convoy——对 RVV 推理场景的新问题重述;
  • 可工作的 V 状态感知 sched_ext 调度器:提出 barrier-safe 抢占延迟这一新调度原语(对 dirty-V 线程的抢占延迟到下一个 barrier 边界,此处切换”免费”),在真实硬件运行;
  • 用户态 → 内核阶段提示接口(可选项 c):ggml 经 pinned BPF map 向调度器声明计算阶段,已实现、已接入、已验证(12 个 worker 全部注册成功);设计空间分析见 phase-hint-interface-zh.md;
  • x86 对比(可选项 d):x86 AMX 提供 TILERELEASE + XFD 可声明扩展状态 dead 并跳过保存,RVV 没有此类指令;本工作的 barrier-safe 延迟是 RVV 侧的软件推广,并为上游 prctl(PR_RISCV_V_DISCARD) 提示提供动机。

详细技术报告见 REPORT-zh.md;完整实验发现(19 项,含被推翻的 5 个预测)见 RESULTS-zh.md。


二、仓库结构

面向 RISC-V Vector 扩展的并发 LLM 推理 V 状态感知调度
├── README.md                                    # 本文件
├── 作品原创承诺书.pdf                            # 作品原创承诺书
├── 演示PPT.pptx                                 # 演示 PPT
├── 项目说明书.docx                               # 项目说明书
├── vstate-sched/
│   ├── REPORT-zh.md / REPORT.md                 # 技术报告(问题分析 / 实现 / 评测 / 创新)
│   ├── RESULTS-zh.md / RESULTS.md               # 完整实验发现日志(F1–F15,含负结果)
│   ├── SLIDES-zh.md / SLIDES.md                 # 演示视频脚本
│   ├── scx_vstate_design-zh.md / .md            # scx_vstate 调度器设计文档
│   ├── phase-hint-interface-zh.md / .md         # 阶段提示接口设计空间分析(可选项 c)
│   ├── OPTIMIZATION-ROADMAP-zh.md / .md         # 优化路线图与杠杆消融
│   ├── KERNEL-CONFIG-zh.md / .md                # 内核重建配置说明
│   ├── pr22863-ime-analysis-zh.md / .md         # llama.cpp PR #22863(IME2 后端)分析
│   ├── CODE-zh.md / CODE.md                     # 代码清单
│   ├── scheduler/                               # 调度优化本体(必做项 2 进阶)
│   │   ├── scx_vstate.bpf.c                     # V 状态感知 sched_ext 调度器(BPF)
│   │   ├── scx_vstate.c                         # 最小用户态加载器
│   │   ├── build_scx.sh                         # 板上一键构建脚本
│   │   └── ggml-cpu-vstate-instrumentation.patch # ggml 协同插桩补丁(唯一上游源码改动)
│   ├── tools/                                   # 量化工具与放置优化
│   │   ├── vstate_bench.py                      # 并发基准(TTFT / TPS / ITL,均值 ± 标准差)
│   │   ├── vstate_tracer.py                     # V 状态保存代理 tracer(ftrace sched_switch)
│   │   ├── vlen_probe.c                         # 经 vlenb CSR 读取 VLEN
│   │   ├── apply_placement.py                   # CPU 亲和性 / SCHED_FIFO 放置
│   │   ├── place_surgical.py                    # 外科手术式线程级拆分放置
│   │   ├── patch_convoy.py                      # ggml barrier convoy 插桩
│   │   ├── convoy_read.py                       # barrier convoy 计数文件读数
│   │   ├── patch_hints.py                       # ggml 阶段提示接线(GGML_VSTATE_HINTS)
│   │   ├── patch_yield.py                       # barrier 边界协作式让出原型
│   │   ├── probe_vsave.sh                       # V-save 代理测量
│   │   └── run_bench.sh                         # 统一基线基准编排
│   ├── experiments/                             # 实验编排脚本(每个 phaseN*.sh 对应 RESULTS 中同编号发现)
│   │   ├── ab_compare.sh                        # A/B 对比编排
│   │   ├── phase4b.sh                           # F4b:线程数匹配验证
│   │   ├── phase4c_cpuset.sh                    # F4c:cgroup cpuset 分区
│   │   ├── phase4d_surgical.sh                  # F4d:外科手术式线程级放置
│   │   ├── phase5_3b.sh                         # F5:Llama-3.2-3B 验证
│   │   ├── phase6_yield.sh                      # F6:barrier 边界协作式让出
│   │   ├── phase7_multitenant.sh                # F7:多租户核心分区(+52%)
│   │   ├── phase8_pinning.sh                    # F8:核心绑定验证
│   │   ├── phase9_scx.sh                        # F9:scx_vstate 基础验证
│   │   ├── phase9b_scx.sh                       # F9b:scx_vstate + 负载
│   │   ├── phase10_a100.sh                      # F10:A100 核 VLEN 1024 对比
│   │   ├── phase10c_a100min.sh                  # F10c:A100 最简对比
│   │   ├── phase11_hints.sh                     # F11:阶段提示协同验证
│   │   ├── phase12_a100_scx.sh                  # F12:A100 + scx_vstate
│   │   ├── phase12b_a100.sh                     # F12b:A100 追加实验
│   │   ├── phase13_tail.sh                      # F13:尾延迟测量
│   │   ├── phase13b_tail.sh                     # F13b:尾延迟追加
│   │   ├── phase13c_gentle.sh                   # F13c:温和调度尾延迟
│   │   ├── phase14_threads.sh                   # F14:线程数缩放
│   │   ├── phase15_a3repeat.sh                  # F15/A3:3 组配对重复验证
│   │   └── phase_final.sh                       # 决赛统一编排预留
│   └── final/                                   # 决赛阶段整合版本
│       ├── README.md / README.zh.md             # 决赛说明
│       ├── REPORT.md / REPORT.zh.md             # 决赛技术报告
│       ├── phase_final.sh                       # 决赛统一编排
│       ├── scx_vstate.bpf.c / scx_vstate.c      # 调度器本体
│       ├── build_scx.sh                         # 构建脚本
│       ├── ggml-cpu-vstate-instrumentation.patch # ggml 插桩补丁
│       ├── vstate_bench.py / vstate_tracer.py   # 基准 + tracer
│       ├── vlen_probe.c                         # VLEN 探测
│       ├── apply_placement.py / place_surgical.py # 放置工具
│       └── (其他工具副本)

代码可存放于本仓库,或以 submodule 形式链接至同一 GitLink 账号下的其他仓库。


三、运行说明

3.1 环境要求

项目 要求
硬件 SpacemiT K3(8× X100 VLEN 256 + 8× A100 VLEN 1024);其他 RVV 1.0 硬件亦可运行测量工具
操作系统 openKylin(板卡出厂系统),内核 6.18.3
内核配置 需重新构建内核并开启:CONFIG_SCHED_CLASS_EXT=y、CONFIG_DEBUG_INFO_BTF=y、CONFIG_KPROBES=y、CONFIG_KPROBE_EVENTS=y、CONFIG_FUNCTION_TRACER=y、CONFIG_FPROBE=y(详细步骤与依赖见 KERNEL-CONFIG-zh.md;出厂内核上述选项均未开启)
llama.cpp 以 -DGGML_RVV=ON + OpenMP 构建(-march=rv64gcv_zfh_zvfh_zicbop_zihintpause),得到 llama-server
模型 Llama-3.2-1B Q4_K_M(主测试)/ Llama-3.2-3B Q4_K_M(GGUF)
依赖 Python 3(requests);构建调度器需 clang ≥ 14、bpftool、libbpf-dev;重建内核需 pahole ≥ 1.16
权限 tracer 与放置工具需 root(写 tracefs / sched_setaffinity);阶段提示路径要求 server 以 root 运行

3.2 快速开始(复现主结果)

所有命令均在 K3 板卡上、工具目录(vstate-bench/)下执行。

① 统一基线并发扫描(必做项 3)

# 自动完成:performance governor、固定 -t 与 -tb、启动 server、健康检查、
# 客户端固定在计算核心之外、2/4/8 并发 × 3 次重复、输出 CSV + 环境元数据
./run_bench.sh models/llama-3.2-1b-q4km.gguf baseline
# 结果:results/baseline_<时间戳>.csv(TTFT/TPS/ITL 逐请求明细 + 均值 ± 标准差)

② V 状态开销量化(必做项 1)

# VLEN 探测(期望输出 256 / 1024 bit)
gcc -O2 -o vlen_probe vlen_probe.c && ./vlen_probe

# V 状态保存代理测量:以 tracer 包住一次基准,统计非自愿切换、归因抢占者、
# 并按实测 VLEN 换算字节流量(需 root)
sudo python3 vstate_tracer.py --pid <server_pid> --vlen 256 \
    --out trace_c4.json -- python3 vstate_bench.py --conc 4 --reps 2 \
    --url http://127.0.0.1:8080 --out bench_c4.csv

③ barrier convoy 插桩

# 对干净 llama.cpp 源码树应用 ggml hook 补丁(convoy 计时 + 阶段提示)
git apply scheduler/ggml-cpu-vstate-instrumentation.patch   # 或用 tools/patch_convoy.py
# 重新构建后,设 GGML_CONVOY_PROF=<计数文件路径> 启动 server,
# 用 tools/convoy_read.py 读取 [wait_ns, count, max_ns]

④ 优化(a):放置纪律(复现 +10% / +52%)

# 线程数与核心数匹配:server 以 -t 8 -tb 8 启动(X100 8 核)即可复现 F6
# 多租户核心分区(复现 F11,+52%):
#   实例 A:-t 4 -tb 4,绑定核心 0-3;实例 B:-t 4 -tb 4,绑定核心 4-7
sudo python3 apply_placement.py <pid_A> 4 0-3 none
sudo python3 apply_placement.py <pid_B> 4 4-7 none
# 完整编排见 experiments/phase7_multitenant.sh、phase8_pinning.sh

⑤ 优化(b):scx_vstate 调度器(复现 F12/F14)

# 构建(需先完成内核重建,见 3.1)
cd scheduler && ./build_scx.sh        # 产出 ./scx_vstate

# 注意:务必在 llama-server 启动并健康之后再加载调度器
#(朴素调度器会在模型加载阶段饿死 I/O kworker,见 REPORT-zh.md §3.7)
sudo ./scx_vstate &                   # 验证:/sys/kernel/sched_ext/root/ops 显示 vstate

# 阶段提示协同(server 需 root 运行):
#   应用 ggml 补丁后以 GGML_VSTATE_HINTS=/sys/fs/bpf/vstate_hints 启动 server,
#   并设 OMP_WAIT_POLICY=passive 启用被动 barrier
# 完整编排见 experiments/phase11_hints.sh、phase9b_scx.sh

⑥ 尾延迟旋钮(复现 A3:P95 ITL −31%)

# 并发 1 低负载场景:-t 6 -tb 8(给内核 / HTTP 预留 2 核),
# vstate_bench.py 已内置 ITL P50/P95/P99/max 插桩;3 组配对重复见 experiments/phase15_a3repeat.sh

3.3 结果对应关系

每个 experiments/phaseN*.sh 脚本在 RESULTS-zh.md 中产出同编号发现(F1–F15);技术报告 REPORT-zh.md §3 给出汇总表格;final/phase_final.sh 为补全 2/4/8 × 3 次前后对比预留的统一编排。

3.4 常见问题

  • 能否用 QEMU 验证? 可以:qemu-system-riscv64 -cpu rv64,v=true,vlen=256 + 主线 6.12+ 内核(开 CONFIG_SCHED_CLASS_EXT=y)可做 sched_ext 功能验证;按赛题规则 QEMU 数字仅作补充,不计入性能评分。本仓库全部性能数字均来自真实 RVV 1.0 硬件。
  • llama-server 的 --cpu-mask / --cpu-range 为什么无效? 该版本 server 未将线程池参数接入这些选项,绑定须经外部 sched_setaffinity 完成(apply_placement.py)。
  • SCHED_FIFO 为什么没有效果? kernel.sched_rt_runtime_us = 950000/1000000 会对 RT 任务限流 5%,且 kworker 会填上空隙;scx_vstate 作为独立调度类不受 RT throttling 影响。

四、开源组件与原创说明

本作品基于开源软件 llama.cpp(MIT 协议)二次开发,对其源码的唯一改动为 scheduler/ggml-cpu-vstate-instrumentation.patch(在 ggml/src/ggml-cpu/ggml-cpu.c 增加两个由环境变量控制的 hook:barrier convoy 计时与阶段提示写入,默认关闭)。调度器、tracer、基准框架、放置工具与全部实验脚本均为本队独立编写。第三方库引用及自创部分占比的详细说明见《项目说明书》”代码原创说明”一节。

五、演示视频

演示视频(MP4,≤100MB)为单独交付物;视频超过仓库限制,请见网盘链接:https://pan.quark.cn/s/439c16c5dee3。视频脚本见《演示视频脚本》。


本仓库内容不包含任何参赛单位、指导教师及参赛学生可识别信息。

关于
7.4 MB
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9 京公网安备 11010802047560号