Prhub

#31151 Move LoRA cuda-graph buffers and logging into LoRAManager

原始 PR 作者 fzyzcjy 合并时间 2026-07-14 15:56 文件变更 2 提交数 5 评论 1 代码增减 +84 / -70

执行摘要

将 LoRA CUDA graph 缓冲区和日志移入 LoRAManager

在 ModelRunner 重构系列(#31151–#31169)中,目标是逐步将 LoRA 相关初始化从庞大的 ModelRunner 抽取到专用组件中,使每个组件拥有自己的设置逻辑,降低模块耦合。已有 LoRAManager 承担核心 LoRA 管理,但 CUDA graph 缓冲区预分配和日志仍留在 ModelRunner 中;本 PR 补全这一缺口。

值得精读,尤其是学习通过“日志下沉+私有方法”逐步提取组件职责的模式。设计决策中将循环前置在 init_lora_manager 末尾而非保留在 initialize(),有助于未来进一步拆分。

讨论亮点

无 review 讨论。

实现拆解

  1. 提取 CUDA graph MoE buffer 预分配函数:将 ModelRunner._init_lora_cuda_graph_moe_buffers 完全移动为 lora_manager.py 的模块级函数 init_lora_cuda_graph_moe_buffers,该函数遍历模型寻找 FusedMoEWithLoRA 模块并调用 LoRAManager.init_cuda_graph_moe_buffers 实例方法。

  2. 吸收进 init_lora_manager:在 ModelRunner.init_lora_manager() 末尾调用该新函数,替代原本在 initialize() 中的单独调用。保持调用顺序(在 init_memory_pool 之前)不变,因为 init_lora_manager 仍在 pool init 之前执行。

  3. 下沉加载/卸载日志:将 ModelRunner.load_lora_adapter 等方法的日志(记录开始、完成及可用 GPU 内存)移到 LoRAManager 的对应公开方法中。原方法重命名为 _load_lora_adapter(私有),公开方法仅包装日志并委托给私有方法。unload_lora_adapterload_lora_adapter_from_tensors 同理。

  4. 调整内部调用init_lora_adapters 中批量加载时直接调用 _load_lora_adapter(无日志),避免冗余输出。

  5. 更新导入ModelRunnerlora_manager 导入新增的 init_lora_cuda_graph_moe_bufferslora_manager 新增对 get_available_gpu_memory 的导入。

文件 模块 状态 重要度
python/sglang/srt/lora/lora_manager.py LoRA 管理 modified 7.95
python/sglang/srt/model_executor/model_runner.py 运行器 modified 7.17

关键符号

_load_lora_adapter _unload_lora_adapter _load_lora_adapter_from_tensors init_lora_cuda_graph_moe_buffers _init_lora_cuda_graph_moe_buffers

关键源码片段

python/sglang/srt/lora/lora_manager.py core-logic

核心接收方:新增模块级函数 `init_lora_cuda_graph_moe_buffers`,实现日志下沉模式(`load_lora_adapter` 等包装为公开方法,原逻辑变成私有 `_load_lora_adapter`),调整内部调用路径。

# python/sglang/srt/lora/lora_manager.pyclass LoRAManager:
    # ... other methods ...
​
    def load_lora_adapter(self, lora_ref: LoRARef) -> LoRAUpdateOutput:
        """
        公开加载入口,记录日志并委托给私有实现。
        目的是让调用方(ModelRunner)只需要简单委托,日志由 LoRAManager 自身控制。
        """
        logger.info(
            f"LoRA adapter loading starts: {lora_ref}. "
            f"avail mem={get_available_gpu_memory(self.device.type, self.device.index):.2f} GB"
        )
        result = self._load_lora_adapter(lora_ref)
        logger.info(
            f"LoRA adapter loading completes: {lora_ref}. "
            f"avail mem={get_available_gpu_memory(self.device.type, self.device.index):.2f} GB"
        )
        return result
​
    def _load_lora_adapter(self, lora_ref: LoRARef) -> LoRAUpdateOutput:
        """私有实现:包含原有断言、配置加载、权重加载等逻辑。"""
        # ... 原 load_lora_adapter 主体 ...
​
    # 类似的包装也应用于 unload_lora_adapter 和 load_lora_adapter_from_tensors# ===== 新增模块级函数 =====def init_lora_cuda_graph_moe_buffers(
    server_args: ServerArgs,
    model: torch.nn.Module,
    lora_manager: LoRAManager,
    dtype: torch.dtype,
):
    """
    Phase 1 of LoRA CUDA graph init: 预分配共享的 MoE 中间缓冲区。
    必须在 init_memory_pool 之前调用以影响 KV 缓存分配。
    从 ModelRunner._init_lora_cuda_graph_moe_buffers 移动而来。
    """
    from sglang.srt.lora.layers import FusedMoEWithLoRA
​
    max_bs = server_args.cuda_graph_config.decode.max_bs
    max_loras = server_args.max_loras_per_batch
    for module in model.modules():
        if isinstance(module, FusedMoEWithLoRA):
            lora_manager.init_cuda_graph_moe_buffers(
                max_bs, max_loras, dtype, module
            )
            logger.info(
                f"Pre-allocated shared MoE LoRA CUDA graph buffers "
                f"(max_bs={max_bs}, max_loras={max_loras})"
            )
            break
python/sglang/srt/model_executor/model_runner.py data-contract

移除了 `_init_lora_cuda_graph_moe_buffers` 方法,在 `init_lora_manager` 末尾调用新提取的函数,并删除了外部日志包装。

# python/sglang/srt/model_executor/model_runner.pyfrom sglang.srt.lora.lora_manager import LoRAManager, init_lora_cuda_graph_moe_buffersclass ModelRunner:
​
    def init_lora_manager(self):
        # ... 原有 lora_manager 创建 ...
        self.lora_manager = LoRAManager(
            base_model=self.model,
            base_hf_config=self.model_config.hf_config,
            # ... 其他参数 ...
        )
        # 将缓冲区预分配吸收进 init_lora_manager 末尾
        if not cuda_graph_fully_disabled():
            init_lora_cuda_graph_moe_buffers(
                server_args=self.server_args,
                model=self.model,
                lora_manager=self.lora_manager,
                dtype=self.dtype,
            )
​
    # 原 _init_lora_cuda_graph_moe_buffers 方法被删除
    # 原 load_lora_adapter 等方法的日志包装被删除,直接委托给 self.lora_manager
​
    def load_lora_adapter(self, lora_ref: LoRARef):
        """现在只做简单的委托,日志在 LoRAManager 内部。"""
        return self.lora_manager.load_lora_adapter(lora_ref)

评论区精华

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

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

风险与影响

初始化顺序风险init_lora_cuda_graph_moe_buffers 必须在 init_memory_pool 之前调用以正确影响 KV 缓存大小。本 PR 通过将其内联到 init_lora_manager 末尾(仍在 initialize() 早期)保持该约束,但任何未来对 init_lora_manager 的调用顺序调整都可能破坏此契约。缺失测试覆盖:未有新增单元测试验证移动后的正确性,回归风险依赖现有集成测试。接口变更ModelRunner 移除了 _init_lora_cuda_graph_moe_buffers 方法,外部直接调用者会出错;但该方法以下划线开头,可视为私有接口。

对用户无行为影响,API 无变动。对开发者而言,LoRAManager 更自包含,ModelRunner 简化为委托,未来 LoRA 相关的初始化调整只需修改 LoRAManager。团队内的 ModelRunner 分解系列 PR(#31151–#31169)获得一致性模式。

初始化顺序依赖 缺少测试覆盖 私有方法被移除

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论