文章目录
- [04 稳定性与性能调优实录:OOM 崩溃、并发槽位、前缀缓存与 DSpark 调参](#04 稳定性与性能调优实录:OOM 崩溃、并发槽位、前缀缓存与 DSpark 调参)
-
- [1. 事故:长会话把显存顶爆(CUDA OOM)](#1. 事故:长会话把显存顶爆(CUDA OOM))
- [2. 取舍:ncmoe(专家上 CPU)调节阀](#2. 取舍:ncmoe(专家上 CPU)调节阀)
- [3. 可用性事故:单槽串行 + 大系统提示词](#3. 可用性事故:单槽串行 + 大系统提示词)
-
- [`--cache-reuse 256`(跨请求前缀复用)](#
--cache-reuse 256(跨请求前缀复用)) - [`-np 2`(双槽位 + 连续批处理)](#
-np 2(双槽位 + 连续批处理))
- [`--cache-reuse 256`(跨请求前缀复用)](#
- [4. DSpark 草稿调参:n_max 3 → 5](#4. DSpark 草稿调参:n_max 3 → 5)
- [5. 思考控制通道实测(客户端集成必读)](#5. 思考控制通道实测(客户端集成必读))
- [6. 与官方建议对齐清单](#6. 与官方建议对齐清单)
- [7. 现网最终配置(速查)](#7. 现网最终配置(速查))
04 稳定性与性能调优实录:OOM 崩溃、并发槽位、前缀缓存与 DSpark 调参
《DeepSeek-V4-Flash 本地部署与公网服务化》系列 · 第 ④ 篇 / 共 5 篇
① 硬件选型与 llama.cpp 部署 | ② API 鉴权与腾讯云公网桥接 | ③ 多协议支持:Anthropic 与 Responses API | ④ 稳定性与性能调优实录(本篇) | ⑤ 客户端接入:pi 与各类 SDK
本篇是上线后 24 小时内踩的坑与调优全过程,全部附实测数据。结论先行:./serve.sh 8081 524288 4 1 2(DSpark n_max=5 + ncmoe=4 + 双槽位 + cache-reuse)是这台 192GB 机器上速度/稳定/并发的最优点。
1. 事故:长会话把显存顶爆(CUDA OOM)
现象:公网请求突然全部 502(nginx Bad Gateway)。
定位:
-
502 = nginx 连不上上游 → 跳板机 8081 隧道还在,但隧道尽头没东西了 → llama-server 进程已死,tmux 会话
ds消失。 -
排除网络层:隧道 autossh 进程健在;iptables 的
! -i lo规则正确(回环放行、丢包数 0)。 -
日志
/tmp/serve_dspark.log尾部铁证:11.31.561.655 E CUDA error: out of memory
ggml_cuda_error ← ggml_cuda_pool_vmm::alloc
← argsort_f32_i32_cuda_cub ← ggml_cuda_op_top_k ← ... ← llama_decode
崩溃在 top_k 采样的 argsort 显存分配 。DSpark 全 GPU 档(ncmoe=0)每卡只剩 ~2GB 余量(01 篇的显存核算),随着会话上下文增长、KV cache 膨胀,某个 decode 步骤的采样 scratch 分配就越过了红线。llama.cpp 的崩溃处理器用 gdb 抓了完整 backtrace 写进日志,对定位帮助很大(现场备份在 /tmp/serve_dspark.log.crash.oom)。
教训 :--fit off + 理论核算通过 ≠ 运行时安全------KV cache 是随会话长度动态增长的,余量要按"最长会话"而不是"启动时"来留。
2. 取舍:ncmoe(专家上 CPU)调节阀
-ncmoe N 把前 N 个 MoE 专家层放到 CPU(权重 mmap,随用随读),是最有效的显存阀门:
| 配置 | decode | 显存/卡 | 结论 |
|---|---|---|---|
| ncmoe=0 | 45.9 tok/s | 43--48 GB(余量 ~2GB) | 最快,但长会话 OOM 实锤 |
| ncmoe=4 | 25.3 tok/s | 36--46 GB(余量 3--13GB) | 稳定,速度打 55 折 |
两个容易误读的现象(当时用户也疑惑过):
- 内存/CPU"没变化"不代表没生效 :CPU 专家权重走 mmap,常驻 page cache(
free的 buff/cache 列,186GB 里就有它),不进进程 RSS;CPU 只在生成 token 的瞬间工作,空闲时是 0。判断依据是显存确实降了 + 日志有tensor overrides to CPU are used with mmap enabled。 - ncmoe=4 时务必配
numactl --interleave=all(双路 EPYC 8 个 NUMA 节点,避免单节点带宽热点)。
3. 可用性事故:单槽串行 + 大系统提示词
现象:在一个 pi 会话正常使用时,另一个会话发请求"很久都没有反应"。
定位(看 llama-server 的 slot 日志):
task 704 | prompt processing, n_tokens = 49152 ... 167936
t = 101s → 450s,吞吐 485 → 368 tok/s 一路递减
两个叠加问题:
-np 1单槽串行:一个请求的 prefill + 生成没跑完,其它请求全部排队,零反馈。- 大 prompt prefill 昂贵 :pi 这类 agent 客户端的系统提示词 + 工具定义 + 会话历史动辄 80K--170K tokens;168K 的 prompt 光 prefill 就要 7.5 分钟 (吞吐随上下文变长递减,attention 成本上升)。而且之前响应里
cached_tokens一直是 0~2------同样的系统提示词每次请求都从头算。
药方:
--cache-reuse 256(跨请求前缀复用)
把 KV cache 中 ≥256 token 的公共前缀复用给新请求。实测:同一个 ~2050 token 的系统提示词,第二请求 cached_tokens: 1022------一半 prefill 直接跳过。pi 的系统提示词在所有请求/会话间共享,收益巨大。
注意:开启后日志有警告 cache_reuse is not supported by this context, it will be disabled------指的是 DSpark 草稿模型的上下文不支持,主模型的前缀复用不受影响(实测 cached_tokens 正常),属于良性警告。
-np 2(双槽位 + 连续批处理)
-c 524288 -np 2 = 两个 256K 槽位,llama.cpp 的 continuous batching 会把一个槽位的 prefill 与另一个槽位的 decode 混批,多会话不再互相堵死。代价:单会话上下文上限 512K → 256K(槽位间静态平分),客户端的 contextWindow 要同步调小,否则超出会被服务端硬顶回来。
意外收获:总 KV 不变,但显存反而更省(GPU1 余量 2.9 → 9.8GB)------单槽 512K 时某些缓冲区按满配 ctx 分配。
4. DSpark 草稿调参:n_max 3 → 5
官方模型卡的 vLLM 配置用 num_speculative_tokens: 7;llama.cpp 的 --spec-draft-n-max 上限是 5(草稿训练块大小,日志里 block_size=5)。实测(同一路径、同类代码生成任务):
| n_max | decode | 接受率 | 备注 |
|---|---|---|---|
| 3 | 25.3 tok/s | 70%(mean len 3.10) | 原配置 |
| 5 | 34.1 tok/s(+35%) | 44%(mean len 3.75) | 现网配置,显存无变化 |
接受率"下降"是正常摊薄------每步多提 2 个候选 token,命中率自然低,但每步净接受的 token 更多,wall-clock 才是真相。
5. 思考控制通道实测(客户端集成必读)
官方模型卡写明 reasoning_effort 只有 low / high / max 三档。对 llama-server 逐个实测(--reasoning-format deepseek 模式下):
| 请求字段 | 结果 |
|---|---|
reasoning_effort: low/high/max |
✅ 接受(顶层或 chat_template_kwargs 内都行) |
reasoning_effort: medium/minimal/none/off |
⚠️ 200 但静默忽略(回退默认思考) |
reasoning_effort: disabled |
❌ 关不掉思考(reasoning_content 照出) |
thinking: {type: "disabled"}(DeepSeek 官方 API 风格) |
❌ 被忽略,仍思考 |
chat_template_kwargs: {enable_thinking: false} |
✅ 唯一有效的关思考通道 |
两者同传(enable_thinking:false + reasoning_effort:max) |
✅ enable_thinking 胜出 |
一句话:档位用 reasoning_effort(low/high/max),开关用 chat_template_kwargs.enable_thinking,别指望 DeepSeek 官方 API 的 thinking 字段。客户端映射详见 05 篇。
6. 与官方建议对齐清单
| 官方模型卡建议 | 落地 |
|---|---|
temperature=1.0;agentic top_p=0.95、其它 top_p=1.0 |
写入客户端 samplingParams(pi 是 agentic 场景,用 0.95) |
reasoning_effort = low/high/max |
客户端档位映射只发这三个值 |
| high/max 档最大输出 384K tokens | 客户端 maxTokens 设 32768(稳妥值,可按需上调) |
DSpark num_speculative_tokens: 7 |
llama.cpp 上限 5,已调到 5 |
无 Jinja 模板(用 encoding/ 目录 Python 脚本编码消息) |
llama.cpp 内置 DeepSeek V4 模板,无需关心 |
7. 现网最终配置(速查)
bash
./serve.sh 8081 524288 4 1 2
# port=8081, ctx=512K(2槽×256K), ncmoe=4, dspark=1(n_max=5), np=2
# + --cache-reuse 256 + --api-key-file
decode 34.1 tok/s,显存余量 9.8--22.9GB/卡,多会话并发,同前缀 prefill 减半。从"最快但会崩"(46 tok/s @ ncmoe=0)到"稳定且够快",这笔交易是划算的。
上一篇 :03 多协议支持 · 下一篇 :05 客户端接入:pi 与各类 SDK