Deployment log / A100

Qwen3.8 Flash Next / 4×A100 完整部署记录

这是 2026-08-30 至 2026-08-31 在现有 A100-SXM4 节点上的实际替换与调优记录:哪些参数来自模型实现约束,运行期 OOM 怎么处理,以及 64K/c8 实测为什么最终选择 16384/0.88。

2026-08-31端口 30001GPU 0–3
部署结果 原 30001 的 Qwen3.8-27B dense INT8 服务已替换为 Qwen3.8 Flash Next AWQ W4A16。新服务只使用 GPU 0–3;GPU 4–7 和 30000 上的既有大模型服务没有修改。

部署对象与边界

项实际值说明
GPU4× A100-SXM4-80GB固定 GPU 0、1、2、3
公开端口30001沿用原服务端口
模型wtdcode/Qwen3.8-Flash-Next-AWQ-W4A16compressed-tensors pack-quantized;routed experts INT4 g128,其余 BF16
镜像lazymio/vllm-backport:latest-sm80Ampere / A100 专用 backport
镜像 digestsha256:9f23c3fe…7a345部署时解析到的 manifest,避免只记录浮动 tag
主 model IDqwen3.8-flash-next新调用方使用
兼容 model IDqwen3.8-27b-int8-w8a16vLLM 直连兼容;11000 网关另保留 qwen3.8-27b

为什么是 TP4 + EP

这不是单纯为了把模型分到四张卡。该 AWQ checkpoint 的 routed expert intermediate width 为 640,量化 block 为 128×128。只用普通 TP4 会把 expert intermediate 切成每 rank 160,无法满足 FP8 block 对齐,vLLM 会落到不适合 A100 这条量化路径的 kernel。

--enable-expert-parallel 让 experts 保持完整并在 rank 间分布,权重走 Marlin W4A16 路径。因此这里的 EP 是模型布局约束,不是可有可无的性能开关。

为什么需要 PLE CPU offload

Flash Next 含有约 51B 的 n-gram embedding,FP8 后仍约 51 GiB。若由 TP4 分摊到 GPU,每张卡还要增加约 12.8 GiB,直接压缩 KV cache。设置 VLLM_PLE_CPU_OFFLOAD=1 后,这部分由独立 PleOffloadWorker 放在 pinned host RAM,通过 CUDA IPC 参与推理。

代价是主机需要额外约 51 GiB 锁页内存,因此 Compose 同时保留 unlimited memlock。它不是传统的整层 CPU offload,不能据此推导所有模型都能用同样方法省显存。

实际 Compose

下面去掉了内部 registry 域名和宿主绝对路径,其余关键参数与运行配置一致:

services:
  qwen3_8_flash_next:
    image: lazymio/vllm-backport:latest-sm80
    container_name: vllm_qwen3_8_flash_next
    restart: unless-stopped
    ports:
      - "30001:30001"
    volumes:
      - /path/to/Qwen3.8-Flash-Next-AWQ-W4A16:/model:ro
      - /path/to/vllm-cache:/root/.cache
      - /path/to/triton-cache:/root/.triton
      - ./chat-template.jinja:/opt/chat-templates/froggeric/chat_template.jinja:ro
    environment:
      VLLM_PLE_CPU_OFFLOAD: "1"
      OMP_NUM_THREADS: "1"
      VLLM_WORKER_MULTIPROC_METHOD: spawn
    cap_add:
      - SYS_PTRACE
    ipc: host
    shm_size: 32g
    ulimits:
      memlock: -1
      stack: 67108864
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids: ["0", "1", "2", "3"]
              capabilities: [gpu]
    command:
      - /model
      - --host=0.0.0.0
      - --port=30001
      - --served-model-name
      - qwen3.8-flash-next
      - qwen3.8-27b-int8-w8a16
      - --tensor-parallel-size=4
      - --enable-expert-parallel
      - --max-model-len=262144
      - --max-num-seqs=8
      - --max-num-batched-tokens=16384
      - --gpu-memory-utilization=0.88
      - --disable-custom-all-reduce
      - --speculative-config={"method":"mtp","num_speculative_tokens":3}
      - --compilation-config={"mode":0,"cudagraph_mode":"FULL_DECODE_ONLY"}
      - --enable-auto-tool-choice
      - --tool-call-parser=qwen3_xml
      - --reasoning-parser=qwen3
      - --enable-prompt-tokens-details
      - --enable-prefix-caching
      - --mamba-cache-mode=align
      - --chat-template=/opt/chat-templates/froggeric/chat_template.jinja

生产使用模型原生 262,144-token context,不再设置静态 YaRN factor 4。默认采样保持 temperature=1.0, top_p=0.95, top_k=20,默认 reasoning effort 为 medium。

调度与显存容量

0.88gpu-memory-utilization
2.96M总 KV token pool
262K单请求最大上下文
8scheduler max sequences

当前 vLLM 分配的 KV pool 合计 2,955,692 tokens,可容纳约 11.28 条完整 262,144-token context,高于 scheduler 的 8 条上限。max-num-seqs=8 仍只表示同时管理的 sequence 数量;实际总 context 必须落在同一个 KV pool 内。

max-num-batched-tokens=16384 是本轮 64K/c8 扫描选出的调度甜点。它不是单请求最大上下文:长 prefill 仍会被 chunked prefill 分步调度。

MTP、CUDA graph 与 cache

LMCache L1 为什么回滚

LMCache L1 曾以 MP connector 接入 30001:800-token chunks、1 TiB host-memory ceiling、没有 L2。用户随后通过兼容 ID qwen3.8-27b 的正常请求观察到重复输出。该 ID 实际仍路由到同一个 Flash Next upstream,因此不能把异常归因于旧模型。

短的新提示词和 8 路并发合成请求没有稳定复现,尚不能证明具体损坏点;但 HTTP 200 和新 prompt 正常都无法排除 hybrid recurrent state 在外部 cache retrieve 时的静默质量错误。生产质量优先,因此先移除 LMCacheMPConnector、retention 参数和 LMCache container,只保留 vLLM 原生 prefix cache。

层级当前值用途
GPU prefix cache保持开启最低延迟的热前缀命中
LMCache L1关闭等待 cache-hit 质量 A/B
LMCache L2关闭不使用 NVMe,不产生 disk2 写放大
主机 available RAM约 1.8 TiB移除 L1 后实测

回滚前还发现 0.5.5.dev45 即使配置 lazy allocation 和 20 GiB initial,也实际向 container charge 约 1017 GiB;模型 ready 后主机 available 仅约 743 GiB。回滚后恢复到约 1.8 TiB。这也是不能只按参数名判断容量的运维证据。

恢复门槛:固定同一长前缀,分别覆盖 cache store、vLLM 重启后 external retrieve、流式与非流式输出,并逐 token 对比无 L1 基线;未通过前不恢复 L1。

启动失败:不是要 privileged

第一次启动在 PleOffloadWorker 报 pidfd_getfd: Operation not permitted。PLE 需要在 sibling processes 间传递 CUDA tensor,Linux 对 pidfd_getfd 做 ptrace 权限检查。最终只增加 SYS_PTRACE capability,同时移除从旧服务继承的 PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True。

没有使用 privileged: true。privileged 不会增加 CUDA core、HBM 或 NVLink 带宽,只会扩大容器权限。问题能由单一 capability 解决,就不应把整个宿主设备和安全边界交给容器。

Chat template 与工具调用

服务继续挂载固定的 Froggeric Qwen3.8 template,默认 reasoning_effort=medium,保留 reasoning history。工具 parser 使用 qwen3_xml,当前生产只承诺 XML 增量工具调用;模板内存在 JSON 分支不代表 parser 已支持 XML/JSON 双格式。

11000 网关如何衔接

外部 OpenAI-compatible 网关新增公开 ID qwen3.8-flash-next,同时保留 qwen3.8-27b。两个 public ID 都路由到 30001 的新 upstream ID,因此旧客户端不需要同步切换。兼容 ID 是接口稳定层,不再表示后端仍运行旧 dense checkpoint。

64K / 8 并发性能报告

客户端使用本地 llama-benchy 0.4.0 直连 30001。每条请求输入 65,536 tokens、输出 512 tokens,并发 8,唯一 prompt、--no-cache、--exact-tg,每个参数点测量一次。表内 prefill 与 decode 都是 8 条请求的 aggregate tok/s。

batched tokensGPU util结果prefilldecode平均 TTFT最大 TTFT峰值
2,0480.90通过7,545.356.639.18s69.49s306
8,1920.90通过11,471.892.325.31s45.70s762
16,3840.90通过13,564.9107.023.27s38.65s948
24,5760.88通过13,858.6113.523.47s37.83s879
32,7680.90运行期 OOM—————
32,7680.85通过13,707.0112.525.17s38.25s905

从 2,048 增加到 16,384 后,prefill 从 7,545 提升到 13,565 tok/s,增幅 79.8%;aggregate decode 从 56.6 提升到 107.0 tok/s,增幅 89.1%;平均 TTFT 从 39.18 秒下降到 23.27 秒,降幅 40.6%。

24,576 相比 16,384 只增加 2.2% prefill,平均 TTFT 反而略高,同时 KV pool 从 11.71 个完整 context 降至 10.20 个。32,768/0.85 能跑,但平均 TTFT 进一步变差,KV pool 只剩 8.46 个完整 context。因此生产没有追逐最高单项吞吐,而是停在 16,384。

运行期 OOM 与显存甜点

batched tokensGPU utilKV tokens完整 262K context观察
8,1920.903,350,03512.78通过
16,3840.903,069,11211.71通过
24,5760.882,674,01910.20通过
32,7680.902,501,2599.54执行时 OOM
32,7680.852,218,8348.46通过,余量很小
16,3840.882,955,69211.28当前生产

32,768/0.90 在执行阶段需要额外分配 1.19 GiB,当时每张 GPU 仅剩约 0.9–1.0 GiB,因此 OOM。把 gpu-memory-utilization 降到 0.85 后相同 scheduler budget 能完成测试,证明修复方向是少预留 KV、给 activation 和 CUDA graph 留运行期余量,而不是降低模型最大上下文或增加 allocator workaround。

生产甜点:max-num-batched-tokens=16384、gpu-memory-utilization=0.88。前者已经拿到几乎全部吞吐和 TTFT 收益,后者给运行期分配留余量;2,955,692-token KV pool 仍可容纳 11.28 条完整 context,高于 max-8。

数据边界每个参数点只有一组测量,小差异只表示方向,不是统计显著结论。--exact-tg 会强制越过自然 EOS,因此重复文本不能用于质量判断。本轮没有采集 MTP acceptance,也不从 decode 吞吐反推 acceptance。最终 16,384/0.88 已确认 API-ready 和 KV 容量;吞吐数字来自相同 scheduler budget 的 16,384/0.90 实测。

262K × 8 流实测

选定 16,384/0.88 后又补了一组满上下文测试:每条请求指定 261,600 prompt tokens 和 512 output tokens,并发 8。关闭 warm-up、coherence 和 latency probe,确保服务端只收到一组 8 请求。测试完成,容器没有 OOM 或重启。

9,073prefill 总 tok/s
19.3端到端 decode 总 tok/s
134.30s平均 TTFT
230.65s最大 TTFT

19.3 tok/s 不是八条请求都完成 prefill 后的纯 decode 性能。FCFS/chunked prefill 让请求先后进入 decode,512-token 输出与其余请求的超长 prefill 严重重叠;单请求 decode 分布从 2.6 到 64.5 tok/s,已经显示明显的调度不对称。这组数据用于描述 262K 满上下文的实际等待与容量,64K/c8 扫描仍用于选择 scheduler 参数。

回滚边界

旧 Qwen3.8-27B compose 和模型文件仍保留,30000 服务从始至终未动。回滚只需要停止 30001 的 Flash Next container,再从旧 compose 恢复同一端口;不要在回滚时清理模型、编译缓存或修改 GPU 4–7。

当前保留:TP4 + EP、PLE CPU offload、MTP3、262,144 max context、16,384 batched tokens、0.88 GPU memory、FULL_DECODE_ONLY、vLLM 原生 prefix cache、XML tool parser。当前关闭:LMCache L1/L2 和静态 YaRN。待单独 A/B:LMCache cache-hit 质量、FULL_AND_PIECEWISE 与真实业务流量分布。