# PR #22567 完整报告

- 仓库：`sgl-project/sglang`
- 标题：[tokenizer] eliminate O(n²) copy in non-incremental streaming
- 合并时间：2026-04-12 14:05
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/22567

---

# 执行摘要

- 一句话：消除非增量流式输出中的 O(n²) 复制开销，显著提升长序列生成性能。
- 推荐动作：该 PR 值得精读，特别是对于关注性能优化和流式输出实现的工程师。关键设计决策包括：1. 基于性能剖析数据驱动优化；2. 安全地传递引用而非复制，依赖于 asyncio 单线程假设；3. 延迟文本生成以避免每步 O(n) 字符串重建；4. 将路径拆分为三种情况以平衡正确性和性能。建议关注 `_handle_batch_output` 中的条件分支逻辑和 `_wait_one_response` 中的延迟解析实现。

# 功能与动机

基于 PR #22548 的堆叠分析，性能剖析显示 tokenizer 管理器的 `_handle_batch_output` 函数在非增量流式输出（默认路径）中，`state.output_ids.copy()` 和 `state.get_text()` 操作占用了每步 94% 的成本，且随序列长度线性增长，导致总复杂度为 O(n²)。这使得 def-stream 模式在 16k 输出 token 时比 iso-stream/nostream 慢 2.3 倍。关键发现是：没有流式消费者会读取中间块的 `output_ids`，且 `_wait_one_response` 会丢弃除最后一个 `out_dict` 外的所有中间副本，因此中间复制是无效开销。

# 实现拆解

修改集中在 `python/sglang/srt/managers/tokenizer_manager.py` 的 `_handle_batch_output` 函数中，将非增量流式路径拆分为三种情况处理：1. 增量流式（保持不变）：传递 delta output_ids 和 delta text，成本 O(1)；2. 非增量流式且已完成：执行 `.copy()` 和 `get_text()`，成本 O(n) 但仅一次；3. 非增量流式中间步骤：传递 output_ids 引用（无复制）并延迟 text 生成（设为 None），成本 O(1)。同时更新 `_wait_one_response` 函数，在中间步骤延迟解析 text 时通过 `state.get_text()` 在 yield 前解析。该优化同时应用于 `BatchStrOutput` 和 `BatchTokenIDOutput` 路径。

关键文件：
- `python/sglang/srt/managers/tokenizer_manager.py`（模块 srt/managers）: 这是唯一修改的文件，包含了核心优化逻辑，涉及 tokenizer 管理器的输出处理路径，直接影响流式输出性能。

关键符号：_handle_batch_output, _wait_one_response


# 评论区精华

根据提交历史，review 过程中有两个关键修改：1. 由 hnyls2002 提交的 'use sentinel None for deferred text instead of key absence'，将延迟文本的表示从键缺失改为使用哨兵值 None，这可能提高了代码清晰度或兼容性；2. 由 hnyls2002 提交的 'skip text injection for BatchTokenIDOutput streaming'，针对 BatchTokenIDOutput 流式路径跳过了文本注入，可能进一步优化了性能或逻辑一致性。这些修改显示了对实现细节的精细调整，以确保正确性和性能。

- 延迟文本生成的表示方式 (design): 采用 sentinel None 代替键缺失，可能提高了代码清晰度或兼容性。
- BatchTokenIDOutput 流式的文本注入 (correctness): 优化了 BatchTokenIDOutput 的逻辑，避免不必要的文本处理。

# 风险与影响

- 风险：1. 正确性风险：传递 output_ids 引用而非复制依赖于 asyncio 事件循环的单线程特性，如果未来引入多线程或并发修改，可能导致数据竞争。2. 兼容性风险：延迟 text 生成（设为 None）可能影响依赖中间 text 的消费者，但 PR body 指出没有流式消费者读取中间 output_ids，且 Responses API 能正确处理引用。3. 回归风险：修改涉及核心输出路径，如果逻辑拆分有误，可能影响流式输出的正确性，尤其是增量与非增量、中间与完成状态的边界条件。4. 性能风险：优化消除了 O(n²) 瓶颈，但剩余开销来自 SSE 序列化和 HTTP 分块编码，可能仍需后续优化。
- 影响：1. 性能影响：在 16k 输出 token 的基准测试中，def-stream 模式的输出 token/s 从 8,004 提升到 14,618（+82.6%），TTFT 从 69,959ms 降到 1,159ms（-98.3%），显著改善长序列生成性能。2. 用户影响：使用默认非增量流式输出的用户将体验到显著的延迟降低和吞吐量提升，尤其对于长文本生成场景。3. 系统影响：优化减少了 tokenizer 管理器的 CPU 开销，可能降低整体系统负载。4. 团队影响：提供了性能剖析和优化的范例，展示了如何识别和消除 O(n²) 瓶颈，对类似性能优化有参考价值。
- 风险标记：核心路径变更 , 依赖单线程假设 , 边界条件复杂

# 关联脉络

- PR #22548 [tokenizer] 未提供，但 PR body 提到 'Stacked on #22548': PR body 明确指出该 PR 堆叠在 #22548 之上，可能 #22548 是前期性能剖析或相关优化。
- PR #22497 fix prefill tps log accuracy: 同属 observability 和性能相关优化，涉及调度器指标和日志准确性。
- PR #22577 Add hisparse staging + decode offload guards to is_fully_idle(): 同属调度器相关优化，涉及空闲检测逻辑，可能共享性能改进主题。