Prhub

#32415 [VLM] Split multimodal scheduling from mm_utils

原始 PR 作者 mickqian 合并时间 2026-08-05 20:24 文件变更 4 提交数 2 评论 2 代码增减 +691 / -668

执行摘要

多模态调度逻辑从 mm_utils 拆分为独立模块,纯重构

mm_utils.py 已增长到 2,090 行,混合了通用多模态辅助函数与 cross-request ViT batching 引入的延迟敏感调度路径。PR body 明确说明动机:“给该路径一个独立模块可以使后续调度优化和 review 更聚焦,而不改变运行时行为。” 这是为后续调度优化铺路的模块边界重构。

值得快速浏览而非精读:关注 mm_schedule.py 的模块边界划分、mm_utils.py 的兼容 re-export 手法,以及测试中猴补丁隔离 CUDA-IPC ack 的做法。建议后续跟踪 mm_schedule.py 上陆续出现的调度优化 PR,并留意仓库是否会在后续清理 mm_utils 的 re-export。

讨论亮点

本 PR 没有任何 review 评论;两条 issue 评论分别是 gemini-code-assist bot 的停用通知和作者自己的 /tag-and-rerun-ci 命令。核心“讨论”来自 PR body 的三点设计声明:一是用 AST 等价校验保证纯移动的无行为变化;二是刻意保留 mm_utils.init_mm_embedding_cache 兼容导入;三是 CPU 测试通过 monkeypatch 隔离 CUDA-IPC ack,并指出该测试在当前 origin/main 上同样会失败,属于对测试改动的主动说明。

实现拆解

  1. 新建 python/sglang/srt/managers/mm_schedule.py(+658 行):迁入 16 个调度相关定义,包括模块级 embedding_cacheinit_mm_embedding_cacheget_embedding_chunk_get_precomputed_embedding_flatten_embedding_result_can_skip_pre_embed_feature_move_move_items_to_device_acknowledge_deferred_cuda_ipc_cache_hits_get_chunked_embedding_full_get_chunked_embedding_by_item_batch_encode_per_image_missesPerImageRequestInfoget_embedding_and_maskDataEmbeddingFunc 等。模块只保留调度路径所需依赖(schedule_batchmultimodal_cacheruntime_contextutils),并用 docstring 明确职责为“Multimodal embedding scheduling and cache coordination”。
  2. 精简 python/sglang/srt/managers/mm_utils.py(-654 行):删除上述定义及不再使用的 import(如 dataclass/fieldMultimodalDataItemEmbeddingResultMultiModalStaticCacheEVSEmbeddingResultget_parallelget_scheduleis_hipis_npu),保留 pad_input_tokensinit_feature_bufferembed_mm_inputsTensorTransportMode 等通用工具。兼容性处理是文件头部新增 from mm_schedule import (init_mm_embedding_cache as init_mm_embedding_cache) 以及 DataEmbeddingFuncget_embedding_and_mask 的 re-export,保证外部调用 mm_utils.init_mm_embedding_cache(...) 仍可用;但只 re-export 了 3 个符号,旧代码若直接引用 mm_utils._get_chunked_embedding_fullmm_utils.embedding_cache 等会断裂。
  3. 更新缓存初始化调用点 python/sglang/srt/mem_cache/kv_cache_builder.py:单行 import 从 mm_utils 改为 mm_schedule,确保 KV cache builder 初始化时引用新模块持有的 embedding_cache 全局对象。
  4. 测试配套 test/registered/chunked_prefill/test_mm_chunked_embedding_unit.py:所有用例从 mm_utils 切换到 mm_schedule,并新增 autouse fixture _skip_cuda_ipc_acknowledgement,用 monkeypatch 把 _acknowledge_deferred_cuda_ipc_cache_hits 替换为空操作。原因是 CPU-only 测试不初始化 tensor-parallel 运行时状态,而该函数会调用 get_parallel(),在无并行状态的 CPU 环境下会失败;PR body 指出当前 origin/main 上该测试同样需要这个隔离才能通过。
  5. 验证手段:16 个被迁移定义与 origin/main 做 AST 等价性检查;pre-commit、compileall、git diff --check 通过;远程 NVIDIA devbox 上运行该测试 5 passed。未附带性能数据,因为本 PR 只修改模块归属,不影响运行时行为。
文件 模块 状态 重要度
python/sglang/srt/managers/mm_schedule.py 调度模块 added 8.98
python/sglang/srt/managers/mm_utils.py 通用工具 modified 8.49
test/registered/chunked_prefill/test_mm_chunked_embedding_unit.py 单元测试 modified 5.07
python/sglang/srt/mem_cache/kv_cache_builder.py 缓存构建 modified 4.03

关键符号

init_mm_embedding_cache get_embedding_chunk _get_precomputed_embedding _flatten_embedding_result _can_skip_pre_embed_feature_move _move_items_to_device _acknowledge_deferred_cuda_ipc_cache_hits _get_chunked_embedding_full _get_chunked_embedding_by_item _batch_encode_per_image_misses get_embedding_and_mask

关键源码片段

python/sglang/srt/managers/mm_schedule.py module-creation

本 PR 的核心产物:新建模块迁入全部 16 个多模态调度定义,是后续跨请求 ViT 批量编码等调度优化的主战场,模块边界的合理与否直接决定重构价值。

"""Multimodal embedding scheduling and cache coordination.本模块由 mm_utils.py 拆分而来:集中放置多模态 embedding 缓存协调与
chunked-prefill 的延迟敏感调度路径(跨请求 ViT 批量编码、chunk 组装与对齐)。
本 PR 仅做模块级代码移动,16 个定义已与 origin/main 做 AST 等价校验。
"""from dataclasses import dataclass, field
from typing import Callable, Dict, List, Optional, Tupleimport torchfrom sglang.srt.managers.schedule_batch import MultimodalDataItem
from sglang.srt.mem_cache.multimodal_cache import EmbeddingResult, MultiModalStaticCache
from sglang.srt.multimodal.evs import EVSEmbeddingResult
from sglang.srt.runtime_context import get_parallel, get_schedule
from sglang.srt.utils import is_hip, is_npu
from sglang.utils import logger_is_hip = is_hip()
_is_npu = is_npu()# 模块级 embedding 缓存;原来是 mm_utils.embedding_cache,搬迁后全局对象
# 归 mm_schedule 所有,旧模块通过 re-export 保持兼容
embedding_cache: Optional[MultiModalStaticCache] = None
​
​
def init_mm_embedding_cache(max_size: int = 0):
    global embedding_cache
    embedding_cache = MultiModalStaticCache(max_size)
​
​
def _acknowledge_deferred_cuda_ipc_cache_hits(items: List[MultimodalDataItem]) -> None:
    """缓存命中跳过 ViT 时,释放惰性的 Kimi IPC 切片。    encoder-DP miss 时只有一个 rank 拷贝图像并对整个 TP 组确认;
    缓存命中时没有任何 rank 拷贝,因此由 rank 0 执行等价的单次确认,
    以维持固定池的生命周期,同时避免引入不必要的 GPU-to-GPU 拷贝。
    """
    parallel = get_parallel()
    if parallel.attn_tp_rank != 0:
        return
    # 池的回收器按整个 TP 组计数,确认次数必须匹配,即使注意力子组更小
    consumer_count = max(parallel.tp_size, 1)
    for item in items:
        item.acknowledge_deferred_cuda_ipc_feature(consumer_count)
python/sglang/srt/managers/mm_utils.py compat-re-export

删除 654 行并保留兼容 re-export,决定外部引用是否断裂;是本次重构的风险集中点。

# 保留既有初始化 import,供下游调用者继续使用(兼容性重导出)。
# 注意:只有 DataEmbeddingFunc、get_embedding_and_mask、init_mm_embedding_cache
# 三个符号被 re-export;若外部代码仍从 mm_utils 引用 _get_chunked_embedding_full
# 或 embedding_cache,会直接 ImportError 或拿到 None。
from sglang.srt.managers.mm_schedule import (
    DataEmbeddingFunc,
    get_embedding_and_mask,
)
from sglang.srt.managers.mm_schedule import (
    init_mm_embedding_cache as init_mm_embedding_cache,
)

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

兼容性风险(主要):mm_utils 只 re-export 了 DataEmbeddingFuncget_embedding_and_maskinit_mm_embedding_cache 三个符号;仓库内若有其他模块直接 from mm_utils import _get_chunked_embedding_full / embedding_cache / get_embedding_chunk,会立即 ImportError 或拿到 None。PR 只改了 4 个文件,未提供全仓引用扫描证据,CI 是主要防线。
全局对象迁移风险:embedding_cache 的所有权从 mm_utils 转到 mm_schedule,任何仍从 mm_utils 读取该全局对象的代码都不会再看到缓存实例,可能表现为缓存命中率下降或 AttributeError。
测试盲区:monkeypatch 屏蔽了缓存命中时的 CUDA-IPC ack 生命周期逻辑,CPU 单测无法覆盖该路径;这里仍依赖 GPU e2e 测试保障,回归后 CPU 测试不会暴露。
运行时与性能风险:按 PR 声明(AST 等价 + 无性能数字),运行时行为与性能均无变化。

对用户与系统:无运行时行为、无性能影响,属于纯代码组织结构变更。
对代码结构:mm_utils.py 从约 2,090 行缩减(删除 654 行),新的 mm_schedule.py 成为多模态调度路径的唯一归属模块,chunked-prefill 分类、跨请求 ViT 批量编码、embedding 对齐等后续优化将集中在此进行。
对团队:调度相关 PR 的 diff 和 review 上下文更聚焦,模块边界更清晰;但一次性移动大量代码也可能让“旧符号引用”问题转移到下游 PR 中解决。
对测试:CPU 单测通过 monkeypatch 摆脱 TP 运行时依赖,稳定性和独立运行能力提升。

纯移动重构,AST 等价校验通过 mm_utils 仅 re-export 三个符号 embedding_cache 全局对象迁移 CUDA-IPC ack 逻辑被测试 monkeypatch 屏蔽

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论