CPU 与内存
大模型推理最显眼的硬件通常是 GPU,但 CPU 和系统内存并不是“配角”。
它们影响模型加载、请求处理、tokenizer、调度、RAG 链路、日志监控,以及显存不足时的 offload 能力。
可以先用一句话理解:
GPU 负责主要矩阵计算,CPU 和内存负责把请求、数据、权重和服务流程稳定地喂给 GPU。
如果 CPU 太弱、内存太小、PCIe 拓扑不合理,即使 GPU 很强,也可能出现启动慢、吞吐上不去、延迟抖动、请求排队甚至服务 OOM。
1. CPU 在推理服务中的作用
LLM 推理中的核心计算通常在 GPU 上完成,但一次在线请求并不是只有矩阵乘法。
CPU 常参与这些环节:
- HTTP / gRPC 请求解析。
- 鉴权、限流、路由和队列管理。
- prompt 模板拼接和参数校验。
- tokenizer 编码和 detokenizer 解码。
- 推理框架的 batch 调度。
- 采样逻辑中的部分控制流程。
- 返回结果拼装、流式输出和日志记录。
- RAG 场景中的检索、重排、上下文构造。
- 监控指标、trace、审计和业务侧回调。
因此,GPU 利用率低不一定说明 GPU 不够强,也可能是 CPU 侧没有及时把任务准备好。
常见表现包括:
- GPU 利用率忽高忽低。
- 首 token 延迟偏高。
- 高并发下请求排队,但显存仍有余量。
- tokenizer 进程或 Web 服务进程 CPU 占用很高。
- RAG 服务中检索、重排或上下文拼装拖慢整体链路。
2. tokenizer 和请求处理开销
tokenizer 是 CPU 侧非常常见的瓶颈。
用户输入的文本需要先被切分成 token,模型输出 token 后也需要被还原成文本。
tokenizer 开销受这些因素影响:
- 输入文本长度。
- 并发请求数。
- tokenizer 实现是否高效。
- 是否启用 fast tokenizer。
- 是否有大量 prompt 模板拼接和字符串处理。
- 是否在 Python 单进程中串行处理。
对于短输出、强并发的在线服务,tokenizer 和请求调度可能占到相当可观的延迟比例。
如果模型本身很小,或者 GPU 很强,CPU 侧瓶颈会更明显。
优化方向通常包括:
- 使用高性能 tokenizer 实现。
- 避免重复 tokenization,对固定系统提示词做缓存。
- 减少不必要的字符串拼接和 JSON 反复序列化。
- 将 API 服务、检索服务和推理引擎合理拆分。
- 给推理服务预留足够 CPU 核数,不要和其他重任务混跑。
3. 系统内存用于什么
系统内存不是显存,但在模型服务中仍然很关键。
系统内存常用于:
- 模型权重从磁盘加载到进程时的临时占用。
- 文件系统缓存,加速权重重复读取。
- CPU offload 的权重或 KV Cache。
- 多进程推理服务的进程开销。
- tokenizer、请求队列、日志和监控数据。
- RAG 检索中的索引、缓存和中间结果。
- 数据预处理、评测任务和批量任务。
一个容易踩坑的点是:模型最终进入显存,不代表加载过程中完全不吃系统内存。
加载大模型时,权重文件、反序列化过程、框架临时对象、文件缓存都可能让内存瞬间升高。
如果内存不足,可能出现:
- 模型加载阶段被系统 OOM killer 杀掉。
- 容器内存限制触发 OOM。
- 启动速度极慢,系统频繁 swap。
- 多个模型副本无法同时加载。
- CPU offload 后延迟严重抖动。
4. CPU offload
CPU offload 指把部分权重、KV Cache 或中间数据放到系统内存中,需要时再通过 PCIe 传回 GPU。
它的主要作用是缓解显存不足:
显存放不下
-> 一部分数据放到系统内存
-> 计算时再搬到 GPU
offload 可以让模型“勉强跑起来”,但通常会牺牲速度。
原因是系统内存到 GPU 显存之间要经过 PCIe,带宽和延迟都远不如 GPU 本地显存。
适合使用 offload 的场景:
- 本地实验,目标是能跑而不是高吞吐。
- 偶尔加载比显存略大的模型。
- 低并发、低 QPS 的内部工具。
- 临时验证模型效果。
不适合作为主要方案的场景:
- 在线高并发服务。
- 严格低延迟服务。
- 长上下文、大 batch 推理。
- 已经对吞吐和稳定性有明确 SLA 的生产服务。
实践中可以记住:offload 是兜底手段,不是免费的显存扩展。
5. NUMA 为什么重要
NUMA 是 Non-Uniform Memory Access 的缩写。
在多路 CPU 服务器上,不同 CPU socket 连接着不同内存通道和 PCIe 设备。一个进程访问“本地”内存会更快,访问另一个 socket 下的内存会更慢。
在 GPU 服务器中,NUMA 会影响:
- CPU 线程访问内存的延迟。
- CPU 到 GPU 的 PCIe 路径。
- 多卡服务的跨 socket 数据传输。
- 网络卡、存储卡、GPU 之间的数据路径。
例如,一张 GPU 接在 CPU 0 的 PCIe root complex 下,但推理进程主要跑在 CPU 1 上,就可能产生跨 NUMA 访问。
这类问题不一定让服务直接失败,但可能导致延迟抖动和吞吐下降。
排查时可以关注:
- GPU 与 CPU socket 的拓扑关系。
- 推理进程绑定在哪些 CPU 核上。
- 内存分配是否靠近使用它的 CPU / GPU。
- 多卡是否跨 socket 通信。
- 网卡是否和目标 GPU 在同一侧拓扑上。
常用思路是让服务进程、CPU 线程、内存分配和目标 GPU 尽量处在同一个 NUMA 节点附近。
6. PCIe root complex 和拓扑
PCIe root complex 可以理解为 CPU 侧连接 PCIe 设备的入口。GPU、网卡、NVMe 等设备都挂在不同的 PCIe 拓扑下面。
这会影响:
- CPU 到 GPU 的数据传输。
- GPU 之间通过 PCIe 通信的路径。
- GPU 与网卡之间的数据路径。
- 多卡并行时的通信均衡性。
- 存储加载模型到 GPU 的路径。
同一台机器里,几张 GPU 不一定完全等价。
有的 GPU 之间路径更近,有的需要跨 CPU socket,有的与高速网卡更近。
在单机多卡或多机推理中,需要特别注意:
- tensor parallel 的 GPU 是否拓扑相近。
- 高速网卡是否靠近参与通信的 GPU。
- NVMe 是否会和 GPU/网卡争用 PCIe 带宽。
- 推理框架选择的 GPU 编号是否符合实际拓扑。
当服务出现“某些卡更慢”“多卡扩展不线性”“跨节点延迟高”时,PCIe 和 NUMA 拓扑都应该纳入排查范围。
7. CPU 推理与 GPU 推理的区别
CPU 也可以跑大模型,尤其是量化模型、本地工具和低并发场景。
但 CPU 推理和 GPU 推理的优势不同。
| 对比项 | CPU 推理 | GPU 推理 |
|---|---|---|
| 主要优势 | 成本低、部署简单、内存容量大 | 矩阵计算强、吞吐高、延迟低 |
| 常见场景 | 本地实验、小模型、低频调用 | 在线服务、高并发、长上下文 |
| 主要瓶颈 | 内存带宽、向量化能力、线程调度 | 显存容量、显存带宽、调度效率 |
| 模型精度 | 常见 INT4 / INT8 量化 | FP16 / BF16 / FP8 / INT8 / INT4 |
| 扩展方式 | 增加 CPU 核数和内存带宽 | 增加 GPU、显存和互联能力 |
CPU 推理并不等于不能用。
如果是 7B 以下量化模型、个人助手、离线批处理、边缘设备或内网低频工具,CPU 推理可能足够。
但如果目标是多人并发、低延迟、长上下文或稳定在线服务,GPU 通常更合适。
8. 内存带宽对本地推理的影响
LLM 推理不是只看 CPU 核数。
对于 CPU 本地推理,内存带宽往往非常关键,因为模型权重需要不断从内存中读取。
可以粗略理解为:
每生成一个 token,都要大量读取模型权重。
内存带宽越高,权重读取越快,token 生成速度越容易提升。
这也是为什么有时“核心很多”不一定快。
如果内存通道少、内存频率低、带宽不足,更多 CPU 线程也可能只是互相争抢内存带宽。
影响 CPU 推理速度的因素包括:
- 内存通道数量。
- DDR4 / DDR5 / LPDDR / HBM 等内存类型。
- 内存频率和实际带宽。
- CPU SIMD 指令集能力。
- 量化格式和推理后端优化。
- 线程数设置是否合理。
- NUMA 绑定是否正确。
本地 CPU 推理选机器时,不要只看“几核几线程”,还要看内存带宽和推理后端是否适配。
9. CPU / 内存选型建议
不同场景下,CPU 和内存的关注点不同。
| 场景 | CPU 建议 | 内存建议 |
|---|---|---|
| 本地学习和实验 | 普通多核 CPU 即可,优先保证兼容性 | 至少能容纳模型文件、运行时和系统开销 |
| 单卡 GPU 推理 | 给 API、tokenizer、调度和监控预留足够核心 | 通常建议大于模型权重文件体积,并留出加载冗余 |
| 多卡 GPU 推理 | 关注 PCIe 通道、NUMA、线程绑定和调度开销 | 需要支撑多副本加载、文件缓存和服务进程 |
| CPU 本地推理 | 关注 SIMD、核心数、内存带宽和线程调度 | 优先看内存带宽,其次看容量 |
| RAG + LLM 服务 | CPU 还要承担检索、重排、JSON 处理和业务逻辑 | 需要容纳索引、缓存、请求队列和模型服务开销 |
| 生产在线服务 | 选择服务器级 CPU,避免和重型批任务混部 | 避免内存打满,预留监控、日志和峰值空间 |
一个实用原则是:
先保证内存够,再看 CPU 是否会卡住请求处理,最后结合拓扑优化多卡和网络路径。
粗略建议:
- 不要让系统长期处在高 swap 状态。
- 不要把 CPU 核数全部压给无关任务,推理服务需要调度余量。
- 大模型加载时给系统内存留出明显冗余。
- 使用 CPU offload 时,系统内存要远大于 offload 量。
- 多卡服务器优先确认 NUMA、PCIe、GPU 和网卡拓扑。
- RAG 服务要把向量库、重排模型和业务服务的 CPU / 内存一起算进去。
10. 常见问题排查
GPU 很空,但服务很慢
可能原因:
- tokenizer 或请求处理成为瓶颈。
- batch 调度不合理。
- RAG 检索链路慢。
- CPU 核数不足或被其他进程抢占。
- Python 服务单进程串行处理过多逻辑。
模型加载时直接 OOM
可能原因:
- 系统内存不足。
- 容器内存限制太小。
- 权重加载存在临时峰值。
- 同时启动多个模型副本。
- 文件缓存、日志或其他进程占用了大量内存。
开启 CPU offload 后能跑但很慢
可能原因:
- PCIe 传输成为瓶颈。
- offload 数据量太大。
- 请求并发或上下文长度太高。
- CPU 内存带宽不足。
- NUMA 绑定不合理,产生跨 socket 访问。
多卡服务有的卡快有的卡慢
可能原因:
- GPU 拓扑不均衡。
- GPU 跨 PCIe root complex 或跨 NUMA 节点。
- 网卡和目标 GPU 距离较远。
- 推理框架选择的 GPU 编号不符合物理拓扑。
- 某些卡还被其他任务占用。
11. 总结
CPU 和内存不会替代 GPU,但会决定 GPU 能不能被稳定、高效地使用。
可以按这个顺序判断主机资源:
- 系统内存是否足够加载模型、承载缓存和服务进程。
- CPU 是否足够处理 tokenizer、请求调度、RAG 和业务逻辑。
- CPU、内存、GPU、网卡和 NVMe 的拓扑是否合理。
- 是否依赖 CPU offload,如果依赖,就要接受明显性能损耗。
- 是否有监控能看到 CPU、内存、swap、NUMA 和 PCIe 相关瓶颈。
真正做硬件选型时,不要只问“GPU 够不够”。
更可靠的方式是把模型规模、并发、上下文长度、推理框架、RAG 链路和服务部署方式一起看,再反推 CPU、内存、GPU、存储和网络。