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=045.9 tok/s43–48 GB(余量 ~2GB)最快,但长会话 OOM 实锤
ncmoe=425.3 tok/s36–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 一路递减

两个叠加问题:

  1. -np 1 单槽串行:一个请求的 prefill + 生成没跑完,其它请求全部排队,零反馈。
  2. 大 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_maxdecode接受率备注
325.3 tok/s70%(mean len 3.10)原配置
534.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: 7llama.cpp 上限 5,已调到 5
无 Jinja 模板(用 encoding/ 目录 Python 脚本编码消息)llama.cpp 内置 DeepSeek V4 模板,无需关心

7. 现网最终配置(速查)

./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

更多推荐