目录

INFERSCHED:面向 RISC-V 的 LLM 推理调度器

本项目面向 Milk-V Jupiter / SpacemiT K1 八核 RISC-V 平台,研究 Linux sched_ext 对 llama.cpp CPU 推理工作负载的调度优化。项目使用 eBPF 调度器识别 执行 RVV 向量计算的 worker,并结合 CPU 分配、vector DSQ、长时间片以及 llama.cpp 提供的 prefill/decode 阶段提示,改善多线程和多模型竞争时的调度行为。

仓库提供一套可复现的实验环境,包括 K1 Linux 6.6 的 sched_ext 补丁、llama.cpp 用户态提示补丁、RVV-aware eBPF 调度器、低开销观测工具和 benchmark。

核心能力

  • K1 sched_ext 内核支持:将 sched_ext 移植到 K1 厂商 Linux 6.6,并补齐厂商树 的调度器、cgroup、BTF、libbpf 和驱动构建兼容代码。
  • RVV worker 识别:根据任务的 RISC-V Vector 上下文状态和可配置 dirty 命中 阈值识别向量计算线程。
  • rvv_dirty_local 单策略:每核唯一定位驱动相位同步推进:为同一 llama-server 的向量 worker 分配互不重复的 CPU 和 per-team/per-CPU vector DSQ,使其稳定地在 各自核上推进、不被迁移或被其它 team 顶替;同一 server 的这组 worker 因此以相近 节奏到达 graph barrier(组内相位 lockstep),显著降低 barrier 自旋/futex 等待。 100ms vector slice 与 500ms local 窗口是这一绑定的配套,不是策略本质。
  • 自动 phase-aware(组间仲裁):PREFILL/DECODE 是 team 级相位,由同一 server 的 worker 共同经历,并非 worker 各自进入。调度器读 ABI v4 phase hint,仅在多个 server 的 team 于同一核相撞时仲裁:PREFILL/MIXED 优先消费、preferred wakeup 定向抢占、prefill 安全片;无有效 hint 时回退默认轮转。无需策略/模式开关。
  • 用户态调度提示:llama.cpp 可通过 mmapable BPF map 注册 OpenMP worker, 并发布 PREFILL、DECODE、MIXED 和 IDLE 阶段。
  • x86/SMT 移植:src/x86_scx_llm_pin 用 ABI v4 hint 替代 RISC-V sstatus.VS 分类,并把 2×4 worker 固定 spread 到 4C/8T 的 0-3/4-7。
  • 调度观测:采集 RVV save/restore 候选、上下文切换、wake-to-run 延迟、 futex wait/wake 和 blocked time。
  • 可重复 benchmark:tests/benchmark 提供 CFS、dynamic、scx-llm-pin、 cfs-affinity 策略,覆盖 dual-async、dual-sync、multi 三种场景,并支持本地 x86 运行。

项目结构

目录 内容
src/kernel_patches/ K1 Linux 6.6 sched_ext 移植、厂商树适配和可选 RVV lazy restore
src/llama_cpp_patches/ llama.cpp OpenMP worker 注册和 prefill/decode phase hint
src/scx_llm_pin/ 主调度器:RVV-aware sched_ext,单策略 rvv_dirty_local + 自动 phase-aware
src/x86_scx_llm_pin/ 同策略的 x86/SMT 自包含移植(ABI v4 hint 分类 + spread 放置)
src/rvv_sched_cost/ RVV、调度延迟和 futex CO-RE eBPF 观测工具
src/dynamic_threads/ 动态授权调度控制器(见附录)
src/x86_dynamic_threads/ x86 动态授权控制器(见附录)
tests/benchmark/ 统一 Python benchmark(三场景,策略 CFS/dynamic/scx-llm-pin/cfs-affinity)
tests/config.sh 板卡、工具链与运行时路径的统一配置来源

工作方式

RVV-aware sched_ext 用户态与 Linux 内核通信架构

scx_llm_pin loader 通过 libbpf 加载 eBPF 程序、初始化并 pin config/workers maps,最后把 struct_ops 附加到 Linux sched_ext。patched llama-server 使用 BPF_OBJ_GET + mmap 写入 OpenMP worker 身份和推理 phase;rvv_lease_ctl 使用 BPF map lookup/update syscall 读取运行状态或在线调整 slice、vector window 和 dirty 阈值(rvv_lease_ctl status / set / reset)。

内核调度核心调用 eBPF 的 select_cpu、enqueue、dispatch、running 和 stopping callbacks。eBPF 调度器结合 BPF maps 与 task_struct 中的 RISC-V Vector 状态完成 RVV worker 识别、CPU 分配和 DSQ 选择,再通过 sched_ext kfunc 把任务交回 Linux 调度核心执行。

Phase-Aware Dirty Lease

Phase-aware lease 中 CPU i 的 DSQ 消费顺序与 vector worker 入队路径

当前策略的核心是相位同步推进(phase-locked progression),而不是单纯把时间片 拉长:把同一 llama-server 的 RVV worker 每核唯一定位后,per-team/per-CPU VDSQ 让 每个 worker 稳定回到自己的 CPU,避免迁移和被其它 team 顶替;于是这组 worker 在各自 分片上以相近速率推进、在 graph barrier 上同步到达(组内 lockstep),barrier 的 “最后到达者”不再被落后者拖住,自旋/futex 等待下降。100 ms vector slice 和 500 ms local 窗口是这一绑定的配套,不是策略本质。PREFILL/DECODE 是 team 级相位,由同一 server 的 worker 共同经历;phase hint 只用于组间仲裁:当多个 server 的 team 在 同一核相撞时选择优先运行的一方。

图从左到右展示 CPU、local DSQ、来源 DSQ 和任务;箭头按真实数据流从右向左。 CPU i 最终只从自己的 local DSQ 运行任务。local 为空时,sched_ext 核心先把 global DSQ 的普通任务转入 local;只有 global 没有提供任务时,BPF dispatch 才从 vector DSQ 消费一个 worker 到 local。图中 VECTOR DSQs (CPU i) 内的 A/B 是源码 中的两条独立队列 VDSQ(A,i) 和 VDSQ(B,i),不是共享 FIFO。

local 窗口在 --vector-local-window-ms > 0(默认 500ms)时始终启用:每消费一次 VDSQ,该 team 的向量任务在随后窗口内改走 local DSQ,与普通任务混排。BPF 优先消费 处于 PREFILL/MIXED 的 team,首选队列为空时回退到另一 team;preferred worker 唤醒且同核正在运行另一 team 的 V worker 时可定向抢占。向远端 VDSQ 投递后按需 kick 目标 CPU。等待达到 1 秒的 runnable V worker 由 pending safety 兜底,普通 任务、helper 和内核任务仍通过 global DSQ 获得服务。以上 phase-aware 分支只在读到 有效 hint 时启用;否则全部回退到 rvv_dirty_local 默认。

调度对比:CFS 与相位同步推进

CFS 与 scx-llm-pin 的时间轴调度对比

上图用同一工作负载的时间轴对比两种调度结果。worker 的执行顺序是计算 → spin → futex,futex 之后才发生上下文切换(红虚线)。

左栏是 CFS:同一 server 的 A0/A1 会竞争同一核、被时间片反复打断,A2/A3 也被 normal 等非向量任务穿插,相位错乱,直到末尾 A3 结束才到达同步点(黄虚线)。 右栏是 scx-llm-pin:同一 server 的 A0–A3 各占一个 CPU 且稳定不被迁移,只在 normal(非向量任务)和早期自旋结束处发生少量上下文切换;A1–A3 各自在自己的分片 上以相近节奏推进,并在第一个同步点(黄虚线,贯穿 A0–A3)对齐、同步完成。

这正是相位同步推进的实现路径:select_cpu/enqueue 把每核唯一定位与 per-team/per-CPU VDSQ 结合,使每个 worker 稳定回到自己的 CPU、不被其它 team 顶替, 于是同一 server 的这组 worker 以相近速率推进并在 graph barrier 上同步到达,而不是 靠拉长时间片。其直接结果是上下文切换大幅下降:板端 dual-sync 短测中本策略把切换 从 CFS 的 40 万级降到 1.3 万级(见下方“上下文切换对比”)。

实验数据对比

以下正式 campaign 使用两个 llama-server 各 8 个 worker 共享 CPU0-7。四个默认 prompt 文本不同,但均构造为 128 input token;每请求固定输出 256 token。A 侧 incumbent 请求达到 token-4 后,按 1 秒间隔向 B 侧注入等量请求。

c=2/4/8 分别运行 60/30/15 个有效轮次,因此每个策略与并发组合都包含 60 个 injected TTFT 样本。TPS 为跨轮 mean +/- sample SD;TTFT mean、sample SD 和 nearest-rank p95 均直接从 60 个 injected 单请求汇总。

c=2

策略 Aggregate TPS (mean +/- SD) Injected TTFT (mean +/- SD) Injected TTFT p95
CFS 1.6300 +/- 0.0340 22.786 +/- 0.291 s 23.195 s
Dirty Lease (observe) 2.5347 +/- 0.0227 (+55.5%) 15.956 +/- 0.483 s (-30.0%) 16.747 s (-27.8%)
Phase-Aware Dirty Lease 2.5250 +/- 0.0168 (+54.9%) 13.904 +/- 0.181 s (-39.0%) 14.253 s (-38.6%)

c=4

策略 Aggregate TPS (mean +/- SD) Injected TTFT (mean +/- SD) Injected TTFT p95
CFS 2.0701 +/- 0.0244 36.513 +/- 12.556 s 49.724 s
Dirty Lease (observe) 2.6995 +/- 0.0176 (+30.4%) 26.975 +/- 9.353 s (-26.1%) 37.767 s (-24.0%)
Phase-Aware Dirty Lease 2.7056 +/- 0.0122 (+30.7%) 21.173 +/- 7.076 s (-42.0%) 28.677 s (-42.3%)

c=8

策略 Aggregate TPS (mean +/- SD) Injected TTFT (mean +/- SD) Injected TTFT p95
CFS 3.2008 +/- 0.0288 76.130 +/- 30.395 s 96.264 s
Dirty Lease (observe) 4.0109 +/- 0.0195 (+25.3%) 50.988 +/- 20.191 s (-33.0%) 65.434 s (-32.0%)
Phase-Aware Dirty Lease 4.0210 +/- 0.0080 (+25.6%) 43.983 +/- 17.556 s (-42.2%) 55.395 s (-42.5%)

observe 读取并校验 worker/phase hint,但不根据 phase 改变调度;完整策略通过 prefill-lease 选择 preferred team,并允许满足条件的 preferred wakeup 定向抢占。 Phase-Aware Dirty Lease 相对 CFS 在 c=2/4/8 下分别提升 TPS 约 54.9%、30.7% 和 25.6%,并将 injected TTFT p95 分别降低约 38.6%、42.3% 和 42.5%。相对 Observe, Phase-Aware 的 TPS 变化不超过 0.4%,但 p95 进一步降低约 14.9%、24.1% 和 15.3%。

上下文切换对比

“每核唯一定位使 worker 稳定驻留、相位同步推进”的一个直接体现是上下文切换大幅下降。 下表为 tests/benchmark 的 dual-sync 短测(2×llama-server 共享 CPU0-7、每请求 128 input / 64 output、各 3 轮)在板端 RISC-V 上的上下文切换总数:

策略 上下文切换(3 轮均值) Aggregate TPS(均值)
CFS 416,758 1.284
CFS + 绑核 + 100ms 长片 + 短 spin 565,473 1.757
dynamic(graph 边界动态授权) 4,888 1.852
scx-llm-pin(本策略) 13,645 1.861

本策略把上下文切换从 CFS 的 40 万级降到 1.3 万级(约 1/30),吞吐随之提升;而 “短 spin”类做法会让 worker 频繁 futex 睡眠/唤醒,切换反而最多(56.5 万)。dynamic 的切换最少,但吞吐与本策略基本持平,说明本策略的核心收益来自稳定绑核下的相位 同步推进,而非压缩切换数量本身。

x86(i5-10200H,2×4 线程、64 output、各 3 轮)趋势一致:CFS 约 84.0k, x86-dynamic 约 3.7k,x86-scx-llm-pin(spread)约 2.4k。

环境准备

硬件与主机依赖

  • Milk-V Jupiter 或其他采用 SpacemiT K1 的八核 RISC-V/RVV 1.0 板卡。
  • x86-64 Linux 构建主机。
  • Git、Repo、Make、CMake、Python 3、Clang、bpftool 构建依赖、OpenSSH 和 sshpass。
  • 支持 -target bpf 的 Clang。
  • 支持 RVV 1.0 intrinsic 和 OpenMP 的 RISC-V GCC 14 交叉工具链,用于构建 llama.cpp。

以下变量用于描述源码、输出目录和板卡连接信息:

export PROJECT_ROOT=/path/to/riscv-rvv
export REPO_ROOT="$PROJECT_ROOT/mxrvvkzddyymxtlbfddyh"
export TOOLCHAIN_ROOT="$PROJECT_ROOT"          # 含 jupiter-buildroot
export BIANBU_ROOT="$PROJECT_ROOT/jupiter-buildroot"
export BOARD_IP="<board-ip>"
export BOARD_USER="<board-user>"
export BOARD_PASS="<board-password>"

板卡连接、工具链与运行时路径也可以直接写进 tests/config.sh (REMOTE_*、TOOLCHAIN_ROOT、LLM_PATH、DT_LLAMA_BIN 等)。所有 Makefile 与 benchmark 都从该文件读取,且环境变量优先。

从 Buildroot 构建完整实验环境

这一节给出从原厂 Buildroot 到可运行实验环境的关键步骤。补丁来源、许可证、完整 构建选项和回退方法分别记录在 src/kernel_patches/README.md 和 src/llama_cpp_patches/README.md。

1. 获取 K1 Buildroot 源码

使用 Milk-V Jupiter 的 k1-bl-v2.1.y manifest:

mkdir -p "$BIANBU_ROOT"
cd "$BIANBU_ROOT"

repo init -u https://github.com/milkv-jupiter/manifests.git \
    -b main -m k1-bl-v2.1.y.xml
repo sync -c -j"$(nproc)"

export KERNEL_SRC="$BIANBU_ROOT/bsp-src/linux-6.6"
export BR_EXTERNAL="$BIANBU_ROOT/buildroot-ext"
export BR_OUT="$BIANBU_ROOT/output/k1_v2"

Buildroot 配置使用 buildroot-ext/configs/spacemit_k1_v2_defconfig,内核源码位于 bsp-src/linux-6.6。

2. 为原厂内核添加 sched_ext

Linux 6.6 主线不包含 sched_ext;该调度类在 Linux 6.12 才正式合入主线。本项目 使用 sched-ext 项目在主线化之前维护的非主线 v6.6.4-scx2 发布树作为移植源, 再适配 K1 厂商 Linux 6.6 的调度器和 cgroup 接口。

内核补丁以 K1 Linux 提交 46ef2dae4ac50c7e556137e4cbbdcc024921d9e3 为准确基线。两个补丁必须按编号顺序 应用:

git -C "$KERNEL_SRC" switch -c sched-ext-rvv \
    46ef2dae4ac50c7e556137e4cbbdcc024921d9e3

git -C "$KERNEL_SRC" am --3way \
    "$REPO_ROOT/src/kernel_patches/0001-sched_ext-port-sched_ext-from-scx-v6.6.4-scx2.patch" \
    "$REPO_ROOT/src/kernel_patches/0002-riscv-complete-sched_ext-integration-and-add-lazy-ve.patch"

第一个补丁移植 sched-ext v6.6.4-scx2 发布树中的 sched_ext 框架;第二个补丁 完成 K1 厂商树适配,并提供可选的 RVV lazy restore 实验功能。

k1_defconfig 已启用 CONFIG_SCHED_CLASS_EXT 和 BTF。Dirty lease 调度实验不依赖 RVV lazy restore;需要评估 lazy restore 时再启用:

"$KERNEL_SRC/scripts/config" \
    --file "$KERNEL_SRC/arch/riscv/configs/k1_defconfig" \
    --enable RISCV_ISA_V_USER_OWNER

3. 构建内核与系统镜像

配置并执行完整 Buildroot 构建:

make -C "$BIANBU_ROOT/buildroot" \
    O="$BR_OUT" BR2_EXTERNAL="$BR_EXTERNAL" \
    spacemit_k1_v2_defconfig

make -C "$BR_OUT" -j"$(nproc)"

应用新的内核补丁后,可清理内核输出并重新生成镜像:

make -C "$BR_OUT" linux-dirclean
make -C "$BR_OUT" -j"$(nproc)"

主要构建产物:

$BR_OUT/images/Image
$BR_OUT/images/bianbu-linux-k1_v2-sdcard.img
$BR_OUT/build/linux-custom/vmlinux
$BR_OUT/host/bin/riscv64-unknown-linux-gnu-gcc

vmlinux 需要包含 .BTF,用于生成 CO-RE eBPF 程序使用的 vmlinux.h。部署 Image 时需要同时部署同一次构建生成的内核模块和 rootfs。

4. 应用并构建 llama.cpp 补丁

llama.cpp 补丁以官方提交 678d43d7205cffc3a8a026cb60b1de3868cc70a2 为准确基线:

export LLAMA_SRC=/path/to/llama.cpp

git clone https://github.com/ggerganov/llama.cpp.git "$LLAMA_SRC"
git -C "$LLAMA_SRC" switch -c sched-ext-hints \
    678d43d7205cffc3a8a026cb60b1de3868cc70a2

git -C "$LLAMA_SRC" am --3way \
    "$REPO_ROOT/src/llama_cpp_patches/0001-llama-publish-sched_ext-worker-and-phase-hints.patch"

使用 GCC 14、RVV 和 OpenMP 构建 llama-server:

export RISCV_CC=/path/to/riscv64-linux-gnu-gcc-14
export RISCV_CXX=/path/to/riscv64-linux-gnu-g++-14
export LLAMA_BUILD=/path/to/build/llama-riscv-hint

cmake -S "$LLAMA_SRC" -B "$LLAMA_BUILD" \
    -DCMAKE_SYSTEM_NAME=Linux \
    -DCMAKE_SYSTEM_PROCESSOR=riscv64 \
    -DCMAKE_C_COMPILER="$RISCV_CC" \
    -DCMAKE_CXX_COMPILER="$RISCV_CXX" \
    -DCMAKE_BUILD_TYPE=Release \
    -DBUILD_SHARED_LIBS=ON \
    -DGGML_OPENMP=ON \
    -DGGML_RVV=ON \
    -DGGML_NATIVE=OFF \
    -DGGML_CPU_KLEIDIAI=OFF \
    -DCMAKE_C_FLAGS="-march=rv64gcv_zfh_zvfh_zba_zicbop -mabi=lp64d -fno-tree-vectorize -fno-tree-loop-vectorize" \
    -DCMAKE_CXX_FLAGS="-march=rv64gcv_zfh_zvfh_zba_zicbop -mabi=lp64d -fno-tree-vectorize -fno-tree-loop-vectorize" \
    -DCMAKE_EXE_LINKER_FLAGS="-latomic"

cmake --build "$LLAMA_BUILD" --target llama-server -j"$(nproc)"

部署时需要使用同一次构建生成的 llama-server、libllama、libggml 和 libggml-cpu。

5. 编译 sched_ext/eBPF 扩展

先在 Buildroot staging 中准备目标端 libbpf、libelf 和 zlib:

make -C "$BR_OUT" libbpf

export STAGING_DIR="$BR_OUT/host/riscv64-buildroot-linux-gnu/sysroot"
test -f "$STAGING_DIR/usr/lib/libbpf.a"
test -f "$STAGING_DIR/usr/lib/libelf.a"
test -f "$BR_OUT/build/linux-custom/vmlinux"

构建过程使用 host Clang 生成 BPF object,使用 host bpftool 生成 skeleton,并使用 Buildroot RISC-V 工具链链接板端 loader。

src/ 下的 Makefile 都从 tests/config.sh 读取 TOOLCHAIN_ROOT(含 jupiter-buildroot)与板卡连接,并内置 fetch-btf:从板卡取运行内核的 /sys/kernel/btf/vmlinux 生成 vmlinux.h。若工具链不在默认位置,先设置 TOOLCHAIN_ROOT 或修改 tests/config.sh。

编译 RVV-aware sched_ext 调度器:

cd "$REPO_ROOT/src/scx_llm_pin"
make clean
make -j"$(nproc)"
make test     # 可选:host 单测(team 配置解析 + hint 客户端),不需要板卡

编译调度与 RVV 观测工具:

cd "$REPO_ROOT/src/rvv_sched_cost"
make clean
make -j"$(nproc)"

产物为 src/scx_llm_pin/{scx_llm_pin,rvv_lease_ctl,rvv_team_exec} 与 src/rvv_sched_cost/rvv_sched_cost。更换内核后,需要重新 make,以便用新内核的 vmlinux 重新生成 vmlinux.h、BPF object 和 skeleton。

x86 上的同策略调度器是自包含工程(用运行内核 BTF、host clang/libbpf/bpftool):

cd "$REPO_ROOT/src/x86_scx_llm_pin"
make
make test

6. 部署并运行调度器

启动包含 sched_ext 补丁的内核后,将调度器部署到板卡:

make -C "$REPO_ROOT/src/scx_llm_pin" deploy

启动 patched llama-server,并指定 worker/phase hint map:

export GGML_SCX_LEASE_HINT_PATH=/sys/fs/bpf/scx_rvv_dirty_lease/workers

/path/to/llama-server \
    -m /path/to/model.gguf \
    -t 8 -tb 8 -np 4

取得 llama-server 的 TGID 后,启动调度器(单策略,phase-aware 自动生效):

# 单模型(旧兼容入口,每模型 8 线程)
/tmp/scx_llm_pin/scx_llm_pin \
    --tgid <llama-server-tgid> \
    --vector-slice-ms 100 \
    --vector-local-window-ms 500 \
    --dirty-threshold 2

# N 模型:PID:decode_threads:prefill_threads[:cpu_mask],可重复最多 8 次
/tmp/scx_llm_pin/scx_llm_pin \
    --team <pid-A>:8:8:0xff \
    --team <pid-B>:4:4:0xff \
    --vector-slice-ms 100 --vector-local-window-ms 500 --dirty-threshold 2

检查 sched_ext 状态、调度器名称和 worker 注册:

cat /sys/kernel/sched_ext/state
cat /sys/kernel/sched_ext/root/ops
/tmp/scx_llm_pin/rvv_lease_ctl status
dmesg | tail -80

rvv_lease_ctl status 应显示完整的 worker TID、OpenMP ith、CPU 映射和 phase。 调度器运行期间,内核日志中不应出现 sched_ext watchdog、stall、BUG 或 Oops。

Benchmark

tests/benchmark 是统一的 Python benchmark(benchmark.py),覆盖三种场景:

--scenario 请求模型
dual-async 双 llama-server,incumbent 先发、到注入 token 后注入第二台
dual-sync 双 llama-server,全部请求共享同一释放时刻
multi 1–8 个 llama-server(JSON 配置),同时释放

策略 --strategy:cfs(默认)、scx-llm-pin(主策略)、cfs-affinity;附录 中的 dynamic 与两个 x86 策略。板卡连接与路径默认从 tests/config.sh 读取。

cd "$REPO_ROOT/tests/benchmark"

# 入口与环境自检
python3 benchmark.py --list-scenarios
python3 -m unittest discover -s tests

# 板端短测:CFS 基线
python3 benchmark.py --scenario dual-sync --strategy cfs \
    --concurrency 2 --loops 1 --output-tokens 16

# 主策略:scx-llm-pin + phase hint
python3 benchmark.py --scenario dual-sync --strategy scx-llm-pin --user-hint on \
    --concurrency 2 --loops 1 --output-tokens 16

每次运行写入 results/<timestamp>_<scenario>_<strategy>_suite/,包含 summary.csv、summary.json、report.md 和每轮 run_metrics.json;有效性门禁 覆盖请求完整性、注入顺序、频率/温度/内存、内核日志以及(开启时)RVV trace 和 调度器事件校验。多 llama 同时到达场景用 --config 指定 JSON 模型列表(示例见 example.json)。指标口径与结果结构见 tests/benchmark/README.md。

双模型 c=4 正式对比

以下两个样例复现“实验数据对比”表中的 c=4 工作负载:两个服务各使用 8 个 worker 共享 CPU0-7,A/B 各承载 2 个请求,A 侧请求均输出第 4 个 token 后注入 B 侧请求,两个不同文本的 prompt 均为 128 input token,每请求固定输出 256 token, 每种策略重复 30 个有效轮次。先设置板端路径:

export LLM_PATH="<board-llama-directory>"
export REMOTE_RUNTIME_DIR="<board-patched-runtime-directory>"
export REMOTE_HINT_RUNTIME_DIR="<board-ABI-v4-hinted-runtime-directory>"
export MODEL_PATH="<board-gguf-model-path>"

cd "$REPO_ROOT/tests/benchmark"

使用 Linux 默认 CFS/EEVDF 调度器运行 30 轮:

python3 benchmark.py \
    --scenario dual-async \
    --strategy cfs \
    --concurrency 4 \
    --loops 30 \
    --output-tokens 256 \
    --injection-token 4 \
    --arrival-gap-sec 1 \
    --runtime-bin-dir "$REMOTE_RUNTIME_DIR" \
    --model-a "$MODEL_PATH" \
    --model-b "$MODEL_PATH"

使用 scx-llm-pin(Phase-Aware Dirty Lease,需要 ABI v4 hinted runtime)运行相同 工作负载:

python3 benchmark.py \
    --scenario dual-async \
    --strategy scx-llm-pin \
    --user-hint on \
    --concurrency 4 \
    --loops 30 \
    --output-tokens 256 \
    --injection-token 4 \
    --arrival-gap-sec 1 \
    --runtime-bin-dir "$REMOTE_RUNTIME_DIR" \
    --scx-hint-runtime-bin "$REMOTE_HINT_RUNTIME_DIR" \
    --model-a "$MODEL_PATH" \
    --model-b "$MODEL_PATH"

两个命令都会自动执行请求完整性、注入顺序、频率、温度、内核日志和服务存活检查; scx-llm-pin 还会校验 worker 注册、phase hint、VDSQ 收支、定向抢占和 pending safety,无效 attempt 不进入汇总。每次运行结束后可在结果目录的 report.md 查看 Aggregate TPS、逐请求 TTFT、pooled p95 TTFT 和上下文切换。

文档

上游项目与许可证

本项目为混合许可证作品:默认许可证为 GPL-2.0-only,其余文件以上游许可证或 文件头部 SPDX-License-Identifier 标识为准。顶层声明见 LICENSE, 第三方组件、版本/基线与版权人见 THIRD_PARTY_NOTICES.md, 许可证正文见 LICENSES/。

内核补丁遵循 Linux 内核 GPL-2.0-only 许可证。llama.cpp 修改遵循上游 MIT License,共享 ABI 头文件使用 GPL-2.0 OR MIT。scx_llm_pin BPF 调度器和 loader 使用 GPL-2.0,rvv_sched_cost 各文件的许可证以其 SPDX 标识为准。补丁来源、版权 和许可证边界见对应补丁目录的 README。

附录:dynamic 与 x86 策略

主策略是 scx-llm-pin;benchmark 另外支持以下对照策略。

dynamic(dynamic_threads 动态授权)

src/dynamic_threads(RISC-V)与 src/x86_dynamic_threads(x86)按 graph 边界 动态授予 OpenMP team 宽度:patched llama-server 通过 Unix socket 与 dt_controller 协商,控制器用 sched_ext 把授权 worker 放到 custom DSQ。板端用法:

cd "$REPO_ROOT/tests/benchmark"
python3 benchmark.py --scenario multi --config example.json \
    --strategy dynamic --output-tokens 64 --loops 3

x86_scx_llm_pin(scx-llm-pin 的 x86 移植)

src/x86_scx_llm_pin 用 ABI v4 hint 替代 RISC-V sstatus.VS 分类,并把 2×4 worker 固定 spread 到 4C/8T 的 CPU0-3 / CPU4-7(每个 server -t 4 -tb 4, --team PID:4:4:0xff)。先构建:

cd "$REPO_ROOT/src/x86_scx_llm_pin"
make
make test

在 x86 主机本地直跑(--local,不经 SSH;加载 sched_ext 需要 root):

cd "$REPO_ROOT/tests/benchmark"
sudo python3 benchmark.py --local --scenario multi --config example.json \
    --strategy x86-scx-llm-pin --output-tokens 64

x86_dynamic(x86 动态授权)

src/x86_dynamic_threads/build/dt_controller 是 x86 版控制器,同样本地直跑:

cd "$REPO_ROOT/tests/benchmark"
sudo python3 benchmark.py --local --scenario multi --config example.json \
    --strategy x86-dynamic --output-tokens 64

example.json 中的 model 需替换为本机可用的 GGUF;x86 场景建议每个模型 threads = threads_batch = 4。

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

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