1. 实测目标与方案
目标只有一个:验证 Qwen3-Omni-Flash 在 “原生四模态” 链路下,端到端延迟能否压到 600 ms 以内,同时给出可复制的参数与踩坑点。
硬件三档:
- Server:4×A100 80 GB,vLLM 0.6.3.post1 + CUDA Graph
- Desktop:1×RTX 4090 24 GB,vLLM 0.6.3.post1 + TensorRT-LLM backend
- Edge:RK3588 NPU 6 TOPS,4-bit 权重 + 8-bit 激活,2×Cortex-A76@2.4 GHz
数据集:自建 1000 条中文多轮对话,平均 7.2 s 音频 + 1.3 张图 / 轮,覆盖安静 / 噪杂(SNR 0–20 dB)、1080p/30 fps 视频片段。
指标定义:
- 首包延迟 TTFB:首个音频采样点离开扬声器的时间 − 首个音频采样点进入麦克风的时间
- 尾包延迟 TTLB:最后一个音频采样点离开扬声器的时间 − 最后一个音频采样点进入麦克风的时间
- 实时因子 RTF:音频总时长 / 模型处理耗时(>1 表示实时)
2. 延迟拆解:链路 5 段耗时
把 211 ms 官方值拆成 5 段后,发现真正可砍的是AuT 编码与MTP 多码本生成。下表为 A100 单路均值:
| 阶段 | 耗时 (ms) | 占比 | 主要瓶颈 |
|---|---|---|---|
| 1. 音频前端(VAD + 重采样 16 kHz) | 18 | 8.5 % | CPU 单线程 |
| 2. AuT 编码(80 ms chunk) | 52 | 24.6 % | GPU GEMM |
| 3. Thinker 推理(20 token 预填充) | 41 | 19.4 % | MoE 路由 |
| 4. Talker MTP 多码本生成 | 67 | 31.7 % | 自回归 12.5 Hz |
| 5. Code2Wav 解码 + 声卡写入 | 33 | 15.8 % | 轻量 ConvNet |
结论:若将帧率从 12.5 Hz 降到 6.25 Hz,可把 4. 砍 15 %(≈10 ms),而 WER 仅 +0.3 %;再对 2. 做 8-bit 权重量化,可再省 8 ms,首包直接压到 190 ms。
3. 三档硬件量化结果
| 硬件 | 精度 | 首包 (ms) | 尾包 (ms) | 并发路数 | 单路功耗 | RTF |
|---|---|---|---|---|---|---|
| 4×A100 | FP16 | 211 | 480 | 128 | 215 W | 2.3 |
| RTX 4090 | FP16 | 234 | 520 | 3 | 135 W | 2.1 |
| RK3588 | INT4 | 420 | 580 | 1 | 8.2 W | 1.2 |
说明:
- 尾包受音频长度影响,测试取 8 s 平均对话;RK3588 采用 30 ms 切片窗口,流式 decode 只缓存 2 帧。
- 并发数由显存 / NPU 内存决定,30B 模型 4-bit 后约 5.8 GB,加上 2 GB 工作缓存,RK3588 8 GB LPDDR4 刚好跑 1 路。
4. 端侧落地:4-bit 量化 + 流式 decode
步骤 1:权重压缩
python3 -m qllm --model Qwen/Qwen3-Omni-30B-A3B-Instruct \
--method gptq --bits 4 --group-size 128 \
--save-q3-omni-4bit.pt
步骤 2:交叉编译 AuT 编码器
- 使用 RKNN-Toolkit2 把 AuT 转成
.rknn,输入 8 kHz/16 bit,chunk 40 ms,输出 512 dim 向量。 - NPU 单帧耗时 7 ms,比 CPU 52 ms 降 86 %。
步骤 3:流式解码缓存 Talker 多码本每 80 ms 生成 1 帧,但 NPU 解码只缓存 2 帧(160 ms),内存占用 < 200 MB;同时把 Code2Wav 换成 1 层 causal ConvNet,MOS 降 0.05,可接受。
步骤 4:系统级优化
- 音频前端 16 kHz→8 kHz 重采样,CPU 占用 –22 %;
- 预填充缓存从 40 token 砍到 20 token,首包 –18 ms;
- 多码本残差剪枝 10 %,显存 –5 %,无感知。
最终 RK3588 实测:尾包 580 ms,满足 “600 ms 以内” 目标,且 RTF=1.2,可连续对话不掉字。
5. 工程清单:一键复现
Docker 镜像
FROM nvcr.io/nvidia/pytorch:24.08-py3
RUN pip install vllm==0.6.3.post1 transformers==4.46.2 \
qwen-omni @ git+https://github.com/QwenLM/Qwen3-Omni
vLLM 启动参数
export VLLM_ATTENTION_BACKEND=FLASH_ATTN
export CUDA_GRAPH_MAX_LEN=20
vllm serve Qwen/Qwen3-Omni-30B-A3B-Instruct \
--tensor-parallel-size 1 --max-num-seqs 256 \
--max-model-len 8192 --dtype float16 \
--enable-chunked-prefill --max-chunked-prefill-len 20
Grafana 面板 JSON(关键指标)
- `vllm:ttfb_seconds{model=