Skip to content

feat: interleave CPU KV cache pages across NUMA nodes - #1399

Merged
hiworldwzj merged 3 commits into
ModelTC:mainfrom
sufubao:cpu_cache_numa
Aug 3, 2026
Merged

feat: interleave CPU KV cache pages across NUMA nodes#1399
hiworldwzj merged 3 commits into
ModelTC:mainfrom
sufubao:cpu_cache_numa

Conversation

@sufubao

@sufubao sufubao commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

CPU KV cache 的共享内存由 prefault 线程首次触碰,物理页落点取决于当时的线程调度。多 Socket 上连续 cache 分块可能集中到单个内存控制器,GPU 并发 load/offload 的带宽和运行间波动都会受影响。

这个改动提供可选的 NUMA interleave 策略:在 prefault 前对 SHM VMA 设置 MPOL_INTERLEAVE,并对每个 attacher 的 VMA 设置相同策略。策略只影响后续缺页,不迁移已分配页。

该功能现在默认关闭,未配置时继续使用 first-touch。需要显式设置:

export LIGHTLLM_ENABLE_NUMA_INTERLEAVE=1

才会调用 mbind。单 NUMA、不支持的架构或 mbind 失败时也会安全回退到 first-touch。旧的 LIGHTLLM_DISABLE_NUMA_INTERLEAVE 已移除,环境变量统一在 envs_utils.py 管理。

E2E

在一台双 NUMA、8×RTX 5090 机器上(GPU 0-3 在 N0,4-7 在 N1),用 InternLM2.5-7B-chat BF16、TP8 对比:

  • A:first-touch
  • B:interleave
  • benchmark commit:4c42ffd2(当时的开关语义为 A 设置 LIGHTLLM_DISABLE_NUMA_INTERLEAVE=1、B 默认启用;当前版本的等价配置为 A 默认、B 设置 LIGHTLLM_ENABLE_NUMA_INTERLEAVE=1
  • image:lightllm:v1.5.3-main-9565289-cu130
  • CPU KV cache:64 GiB,page size 128;并发 16
  • 每个请求 7,909 prompt tokens,其中 7,808 tokens 从 CPU cache 恢复,输出 16 tokens

每个服务实例先分别 seed 两套前缀,再按 set-1→set-2→set-1→set-2 发送 64 个 timed requests。变体外层做 3 组 ABBA,共 12 次独立服务启动、768 个 timed requests。

ABBA 组 A throughput B throughput B vs A A mean TTFT B mean TTFT TTFT 改善
1 15.300 req/s 15.381 req/s +0.52% 648.45 ms 642.38 ms 0.94%
2 14.664 req/s 15.415 req/s +5.13% 680.58 ms 646.06 ms 5.07%
3 14.894 req/s 15.473 req/s +3.89% 669.65 ms 636.26 ms 4.99%

6 次 A/B run 的中位数:

metric A B 改善
Request throughput 14.918 req/s 15.399 req/s +3.22%
Cached-token throughput 116.48k tok/s 120.24k tok/s +3.22%
Mean TTFT 667.08 ms 644.36 ms 3.41%
Mean request latency 738.44 ms 713.97 ms 3.31%

三组 throughput 配对结果都为正,平均改善 3.18%;n=3 的 95% CI 为 [-2.74%, 9.10%],因此这是方向一致的 E2E 收益,但样本量不足以声称统计显著。

768/768 个 timed requests 都命中服务端 CPU cache,没有错误。6 次 B run 的 64 GiB 映射均精确落在 N0/N1 各 8,388,608 页;A 的 N0 占比在 33.4% 到 76.9% 之间。试跑期间发现 --ipc=host 会遗留 SysV SHM,相关数据已排除;正式结果在每次启动前检查可用内存、每次退出后回收对应 SHM。

DMA microbenchmark

同一台机器上,用普通 4K 页测 pinned SHM 的 GPU DMA,每组 3 轮 ABBA:

GPU 组合 聚合带宽 first-touch 中位数 interleave 中位数 变化
0,1,2,3,6,7(跨 NUMA) GPU→CPU offload 144.40 GB/s 170.26 GB/s +17.9%
0,1,2,3,6,7(跨 NUMA) CPU→GPU load 259.33 GB/s 325.44 GB/s +25.5%
0,1,2,3(均在 NUMA0) GPU→CPU offload 114.65 GB/s 143.28 GB/s +25.0%
0,1,2,3(均在 NUMA0) CPU→GPU load 163.22 GB/s 224.43 GB/s +37.5%

interleave 的 numa_maps 每次都是精确 50/50;first-touch 的运行间波动明显更大。E2E 收益小于 DMA 微基准,符合 CPU cache 恢复只是请求链路一部分的预期。

最新精度回归

在当前 head 227e76de 上完成以下全量回归,LIGHTLLM_ENABLE_NUMA_INTERLEAVE 均未设置,用于验证默认 first-touch 路径。

Qwen3.5-0.8B CPU cache + linear attention

GSM8K 全量 1,319 条,在同一服务生命周期连续执行两次:

运行 CPU cache strict exact match flexible exact match
Run 1 1,319/1,319 未命中(写入 cache) 433/1,319 = 0.328279 436/1,319 = 0.330553
Run 2 1,319/1,319 命中 433/1,319 = 0.328279 439/1,319 = 0.332828

两轮 strict accuracy 完全一致,CPU cache 分配、prefault、attach、pinned-memory 注册及全命中路径均正常,没有 cache 异常。

Qwen3-VL-8B-Instruct

MMMU validation 全量 900 条:

模式 mmmu_acc 结果
direct,TP2 439/900 = 0.48778 PASS
ViT separation(config + visual_only + normal TP2) 438/900 = 0.48667 PASS

两个模式 900 条请求均完成并保存逐样本结果。3/900 条原始响应不同,只有 1 条影响正确性;ViT separation 的 438/900 与上一轮回归完全一致,属于单样本运行间波动,没有 visual proxy、AFS、请求或评测错误。

权限与降级行为

另一个双 NUMA、8×H200 的容器环境启用了 Docker seccomp,且没有 CAP_SYS_NICE。在显式启用 interleave 的诊断中,mbindset_mempolicyget_mempolicy 均返回 EPERM;cpuset 允许的内存节点为 0-1,因此失败来自容器安全策略,而不是无效 nodemask。

当前默认关闭配置不会调用 mbind,上述最新回归日志中没有 mbind/EPERM。显式启用但受限时会记录 warning 并安全回退到 first-touch;若需要在该容器覆盖成功路径,应增加 CAP_SYS_NICE 或使用允许 NUMA policy syscall 的定制 seccomp profile。

pre-commit 的 Black、Flake8 检查通过。

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces NUMA node interleaving for CPU KV cache pages in lightllm/utils/kv_cache_utils.py by implementing interleave_pages_across_numa_nodes and helper functions. The feedback highlights two critical issues with the mbind syscall implementation: first, the hardcoded SYS_MBIND syscall number (237) is specific to x86_64 and will cause failures on aarch64 architectures; second, passing 65 as maxnode when nodemask is a 64-bit c_ulong leads to an out-of-bounds read of stack memory, potentially causing mbind to fail with EINVAL.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread lightllm/utils/kv_cache_utils.py Outdated
Comment thread lightllm/utils/kv_cache_utils.py Outdated
@sufubao

sufubao commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

补了 reviewer 要的端到端结果,PR description 里有完整配置和三组数据。

  • 8×RTX 5090、InternLM2.5-7B-chat BF16、TP8、64 GiB CPU KV cache
  • 3×ABBA,12 次独立服务启动,768 个 timed requests
  • 三组 throughput 改善分别是 +0.52%、+5.13%、+3.89%;6-run 中位数 +3.22%
  • mean TTFT 中位数改善 3.41%,mean request latency 改善 3.31%
  • 768/768 请求命中服务端 CPU cache,零错误
  • interleave 的 6 次 64 GiB 映射都精确 50/50;first-touch 的 N0 占比在 33.4%-76.9% 间变化

三组配对 throughput 的平均改善是 3.18%,95% CI [-2.74%, 9.10%]。也就是说,这个 workload 下每组方向一致,并且页落点已经确定性稳定,但 n=3 还不足以把 E2E 收益表述成统计显著。

两条 syscall review finding 已在 4c42ffd2 修复并逐条回复、resolve,pre-commit 通过。请再帮忙看一下;如果没有其他问题,这个版本可以合并。

@hiworldwzj
hiworldwzj merged commit 3b17d8e into ModelTC:main Aug 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants