调度不是一个循环,而是三类循环

SGLang-Omni 为不同计算形状使用不同 scheduler。AR Thinker 或 Talker 使用 OmniScheduler,它把 SGLang 的请求选择、prefill/decode、KV allocator 和结果处理桥接到 Omni payload。普通 encoder、聚合器和轻量函数 stage 倾向使用 SimpleScheduler;持续消费/产生 chunk 的 codec 或 vocoder 使用 StreamingSimpleSchedulerStreamingVocoderBase

这不是三种互换的实现风格。encoder 的主要问题是媒体 batch、显存和重复输入;AR stage 的主要问题是 KV、连续 batching 和 decode 时间;vocoder 还需要保存 per-request window、flush 末尾 chunk,并决定何时可发第一段音频。把它们强行放入一个全局 batch 循环会掩盖这些不同的 SLO。

来源:OmniSchedulerSimpleSchedulerStreaming scheduler

OmniScheduler 如何复用 SGLang

OmniScheduler 没有继承 upstream Scheduler;源码说明它通过未绑定方法和 __getattr__ 委托,借用上游的 batch selection、memory check 和 batch-result processing。它自身再处理 inbox/outbox、Omni request builder、payload projection、stream chunk 与完成事件。这样保留了 SGLang AR engine 的成熟部分,同时允许模型 runner 在 hidden state、codec token 或 Talker feedback 上添加合同。

对 AR stage,这意味着可使用 SGLang 侧的连续 batching、tree/Radix cache、request-to-token pool 和 KV allocator;它不意味着 Omni 自动开启 upstream 的全部 feature。源码明确配置了自己的 compatibility 字段与 feature gate,研究或部署时应检查实际 model runner 和 ServerArgs,而不要只读上游 release note。

AR stage 的责任切分

SGLang bridge:
  选择 prefill/decode batch、管理 KV/token pool、执行模型 forward、采样

OmniScheduler:
  inbox/outbox、Omni payload -> request、结果投影、chunk/terminal 生命周期

model-specific runner:
  输入 embedding、hidden-state side channel、codec token、Talker feedback

已实现的 async decode,与被拒绝的 overlap

当前 enable_async_decode 是一阶段 lookahead:模型 runner 先 launch 一个 decode step,用 CUDA event 在后续 resolve 时读取结果。默认阈值为 batch size 2,batch size 1 走同步路径,因为低并发时额外调度开销可能超过重叠收益。该路径要求 runner 实现相应的 launch/resolve 合同,不能当成对所有 stage 的通用开关。

相反,enable_overlap 在当前源码中不是“实验性可选优化”,而是会直接抛 NotImplementedError。注释给出的原因是 overlap loop 的 Req.inflight_middle_chunks 比结果处理滞后一轮,TTS runner 会静默产生错误 chunk boundary;代码选择拒绝执行而不是输出可能损坏的流。这是已实现的保护,不应宣传为可用的 overlap feature。

同一文件还将 LoRA、speculative decoding、grammar 和 disaggregation 置于关闭状态。因此“Omni 基于 SGLang”只能说明它复用了选定的 AR 基础设施,不能推导出这些功能在 Omni pipeline 中已可部署。

两类缓存:AR KV 与媒体 encoder 结果

AR cache 由上游 SGLang 侧的 tree cache、request-to-token pool 与 KV allocator 管理,作用是可复用 prompt/prefix 的 token state。媒体 cache 则是另一层:它保存已经完成的 image/audio/video encoder 输出,避免同一媒体在不同请求中反复 forward。这两类缓存的键、内存位置、失效条件和收益完全不同,压测必须分别记录命中率。

Qwen stage factory 定义了 CPU LRU 的 encoder 输出 cache,上限为 64 entries、4 GiB。预处理在昂贵解码前对原始 image/audio/video 生成 key;批内若出现相同 key,后到请求成为 waiter,等待首个计算结果。Ming 的预处理也把媒体衍生 key 加入 prompt/cache 语义,避免不同媒体因为共享 placeholder token 而被错误地当成同一 prefix。

一个重要反例是 Qwen Talker:configure_talker_server_args 显式设定 disable_radix_cache=True。不要把 Thinker 的 prefix-cache 结论外推到 Talker,也不要用“缓存已开启”替代每个 stage 的实际 cache policy。

来源:Qwen stage cache 常量与使用StageOutputCache媒体 keyQwen Talker scheduler

组批、TP 与进程并行的限制

stage 可以独立选择 batch 的边界;这有利于让快速 audio encoder 不被长 Thinker decode 拖住,也意味着每个 stage 都可能形成队列。TP stage 的多个 rank 必须以相同顺序接纳请求。为维持这个锁步约束,OmniSchedulertp_size > 1 时把 request-build worker 强制为 1,并禁用基于本地时钟的 prefill coalescing。

同样,stage 级复制、process 级复制和同 GPU MPS data parallel 是不同层次。当前官方 MPS 文档的验证清单不支持 Qwen3-Omni 或 Ming-Omni:Qwen speech 有两个 SGLang engine,而 Qwen text pipeline 没有该 launcher 所需的 generation stage;Ming-Omni 也没有完成同一资格过程。Higgs、MOSS 或 ASR 的 MPS 数字不能迁移为 Omni 模型的部署结论。

Router 在更外层选择完整 worker。它会检查 worker 健康和 capability,例如 audio input、audio output、video input、streaming;对 Ming,它明确不会将一个 request 的 Thinker 和 Talker 分派到不同 worker。它是 ingress 与副本选择器,不替代 pipeline 内部的 stage 并行。

来源:MPS DP 范围Router 文档process-level replica tracking issue。最后一个链接是未完成 tracking,不是已完成 capability。

控制面:消息比 tensor 更早到达

官方通信文档将 SubmitMessageDataReadyMessageCompleteMessageStreamMessageShutdownMessage 和 profiling 信号放在 ZMQ 控制面,abort 使用 PUB/SUB。控制面传达请求身份、状态和数据引用;它并不承载大 tensor 本身。

这个区分让接收 stage 能先知道“哪个 request 的什么 payload 已就绪”,再按引用读取数据。对 streaming 边,先控制后等待还避免了跨节点 credit 等待的环路。研究 fault semantics 时,应验证 data reference ACK、取消、worker 退出和 terminal 聚合是否仍然闭合,而不是只检查 ZMQ 消息是否送达。

数据面:路径取决于进程、节点与 tensor 位置

条件文档描述的传输研究时应检查
同进程LOCAL_OBJECT,Python 对象引用对象只读、生命周期覆盖队列等待;fan-out 不共享可变外层容器。
兼容的同 GPU edgedirect PyTorch CUDA IPCproducer/consumer 生命周期和 IPC handle 存活,不经过 relay ACK。
同节点 GPU 跨进程pooled CUDA IPC relayslot 尺寸、DataRef、ACK 后回收和大 tensor 的多 slot 拼接。
同节点 host/CPU tensorshared memory序列化开销、共享段回收与 abort 时是否泄漏。
跨节点文档选择表指向 Mooncake网络、credit、tail latency 与失败恢复;不能仅凭模块存在宣称生产资格。

源码树还包含 relay/nccl.pyrelay/nixl.py,官方博客也把 shmncclnixlmooncake 列为可用 backend。这里应保持措辞:这是实现/文档列出的 backend 范围;某个模型在跨节点、特定驱动和特定载荷上的稳定表现仍需单独基准和复现。

来源:通信文档relay 基类官方多阶段博客

Qwen:feedback、codec chunk 与 Code2Wav graph

Qwen speech config 启用 Thinker async decode,Talker 使用 feedback,Code2Wav 作为 terminal 流式 vocoder。Talker 将 code chunks 投影给 Code2Wav;后者为每个 request 保存已经接收、已经发声和音频片段的状态,到达阈值时解码新窗口,收到 stream_done 时 flush。stream request 只发送增量音频;非 stream request 最终拼接完整 waveform。

Code2Wav 的 CUDA Graph 不是一般的“开启图模式”。当前配置只捕获精确 [B, Q, T] 形状,默认 B=1 的阈值窗口为 T={10,20,30,35},并在 config 中为其预留 2% typed GPU memory budget。尾部窗口、未捕获 shape 或 capture incompatibility 会走 eager。这是正确性优先的 fallback,性能测量必须分别报告 graph replay 命中率、eager 次数、TTFA 和尾延迟。

Qwen realtime 又是独立输入合同:服务端收 mono 16 kHz PCM16,输出 mono 24 kHz PCM16。视频输入还可选择在预处理时抽取音轨;不要把“视频支持”简化为只抽帧,也不要将 request-level 像素/帧数上限当成模型原生上下文的唯一约束。

来源:Code2Wav schedulerCUDA Graph runnerQwen configQwen usage

Ming:流式取舍和音频格式边界

Ming 的普通 speech pipeline 是完整 utterance Talker;streaming variant 才有 segmenter -> talker_stream。官方 cookbook 说明 generic launch 当前直接暴露默认 speech 和 --text-only,流式 path 尚需要 pipeline config。text-only stream=true 目前输出聚合文本,而不是 token-by-token delta。

该 cookbook 给出带条件的 H100 类实验:TP4 Thinker、独立 Talker、7-stage non-streaming pipeline,Talker 的 SimpleScheduler.max_concurrency=1,高并发吞吐约在 3 req/s 附近平台化。流式方案在并发 1 时更早给出首音频,但吞吐较低,并发增加后队列效应可能反转优势。这里是特定硬件、模型、声音与负载下的方向性证据,不是所有 Omni pipeline 的性能定律。

当前文档还登记了一个明确格式缺陷:Ming Talker 实际生成 44.1 kHz,但 chat-completions 的 WAV header 当前可能按 client 默认 24 kHz 编码;文档建议保存时将 header 设为 44100,而 /v1/audio/speech 路径已转发 rate。部署前应把该例作为音频契约回归测试,而不是假定播放器会自动修复。

来源:Ming cookbook 的 streaming、benchmark 与限制client audio encoding

部署:版本、GPU、Router 与镜像

安装文档推荐 Docker,官方博客示例使用 lmsysorg/sglang-omni:dev、GPU、host IPC 与较大 shared memory。由于 :dev 是移动目标,而正式 release 仍是 0.1.0,严肃复现实验应记录镜像 digest、SGLang-Omni commit、sglang==0.5.16、PyTorch/CUDA、GPU 型号、stage placement、TP、静态显存比例、Graph 配置和模型 snapshot。

当前多硬件 RFC 明确写到 Omni 只在 NVIDIA CUDA 上运行;ROCm、Ascend NPU 与 Intel XPU 还是平台支持设计和后续 model enablement 工作,不能列入当前普遍支持矩阵。Ming 还需要谨慎规划 TP 和独立 Talker GPU;其 cookbook 对 H100 80 GB 的说明是具体模型/配置的容量指导,不是所有 MoE 的通用下界。

Router 适合多个完整 worker 的外层部署:它能启动本地 worker pool 或接入已有 URL,做健康检查、capability 路由、admission bound 和持久化的权重更新 journal。它不会把一个多阶段模型拆散到不同 worker,因此扩容决策仍需结合每个完整 pipeline 的瓶颈。

来源:安装文档Router 文档multi-hardware RFC #1310。RFC 是当前限制和计划的证据,不是非 CUDA 已完成的宣称。

已知限制:按证据等级阅读

等级截至快照的说法正确用法
当前代码OmniScheduler overlap loop 被明确拒绝;LoRA、speculative、grammar、disaggregation 关闭。写成当前实现边界,不写成 SGLang core 永久不支持这些能力。
当前文档Qwen Code2Wav exact-shape graph 有 eager fallback;Ming streaming launch 与音频 header 有明确限制。作为部署前检查和基准维度。
开放 issue#1322 报告 realtime VAD 在相邻 turn 边界可能丢音频;#1360 报告 repetition penalty 可能双重应用。作为可复现风险候选与回归测试,不写成所有版本、所有部署均受影响的已证实结论。
开放性能工作#1378 说明 Qwen Thinker prefill CUDA Graph 仍按路径逐项资格化;#1236 探索 Code2Wav 的 traffic-aware dispatch。用来定义研究缺口,不得宣传为已发布默认优化。

值得做的系统实验

第一组应拆开测量缓存:同一前缀但不同媒体、同一媒体但不同 prompt、同一媒体重复请求,分别报告 SGLang KV/tree hit、Qwen encoder LRU hit、内存占用和输入准备时间。第二组应对同一 Qwen speech workload 比较 direct CUDA IPC、relay CUDA IPC、SHM 与跨节点路径,记录首 token、首音频、完整 waveform、ACK 延迟、abort 后资源回收。

第三组应验证语义正确性而非只跑 QPS:Qwen Code2Wav 的 graph/eager 波形一致性与 tail shape,Ming 的 WAV sample-rate header,VAD 在一个 chunk 内跨两轮语音,TP request admission 顺序,以及重复媒体 cache 是否会错误复用。最后,将 router 的完整 worker 分配与 stage 内部队列指标一起记录,才能知道瓶颈是在 ingress、encoder、Thinker、Talker 还是 vocoder。

一手来源清单