# PR #31870 完整报告

- 仓库：`sgl-project/sglang`
- 标题：[lora] Fix WAR race: never write MoE runner output into hidden_states in place
- 合并时间：2026-07-24 15:58
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/31870

---

# 执行摘要

- 一句话：修复 LoRA MoE 写后读竞争导致生成乱码
- 推荐动作：值得精读，展示了如何诊断 CUDA graph 中的写后读竞争、如何用最小复现定位问题，以及一个配置项即可修复的案例。

# 功能与动机

在 LoRA 长 decode 评测中会出现随机乱码，根源在于 MoE runner 的 inplace 写与 dual-stream 共享专家读构成 CUDA graph 内的写后读竞争。PR body 详细描述了根因和验证。

# 实现拆解

1. **根因定位**：作者通过分析 `forward_normal_dual_stream` 的 stream 边界和 LoRA runner 的 inplace 行为，确认是 `hidden_states` 的写后读竞争。
2. **核心修复**：在 `python/sglang/srt/lora/layers.py` 的 `FusedMoEWithLoRA.__init__` 中，从 base_layer 拿到 `moe_runner_config` 后立即设置 `self.moe_runner_config.inplace = False`，覆盖可能来自 base 的 `inplace=True`。
3. **回归测试**：新增 `test/registered/unit/lora/test_lora_moe_inplace_unit.py`，构造明确 `inplace=True` 的配置，调用真实构造函数，断言 shared config 的 `inplace` 被强制为 `False`，并覆盖 triton fallback 和 marlin 两种 runner core。

关键文件：
- `python/sglang/srt/lora/layers.py`（模块 LoRA 层；类别 source；类型 core-logic；符号 FusedMoEWithLoRA.__init__）: 核心修复文件：在 FusedMoEWithLoRA 构造时强制设置 inplace=False，一行代码消除写后读竞争。
- `test/registered/unit/lora/test_lora_moe_inplace_unit.py`（模块 单元测试；类别 test；类型 test-coverage；符号 _make_base_layer, FusedMoEWithLoRAInplaceTest, _construct, test_constructor_forces_inplace_off）: 新增回归测试，覆盖 triton fallback 和 marlin 两种 runner core，验证构造函数强制 inplace=False。

关键符号：FusedMoEWithLoRA.__init__, _make_base_layer, test_constructor_forces_inplace_off, test_marlin_lora_runner_core_sees_non_inplace_config

## 关键源码片段

### `python/sglang/srt/lora/layers.py`

核心修复文件：在 FusedMoEWithLoRA 构造时强制设置 inplace=False，一行代码消除写后读竞争。

```python
# python/sglang/srt/lora/layers.py
class FusedMoEWithLoRA(BaseLayerWithLoRA):
    def __init__(self, base_layer: FusedMoE, lora_backend: BaseLoRABackend):
        super().__init__(base_layer, lora_backend)
        # ... 省略其他初始化 ...
        self.moe_runner_config = base_layer.moe_runner_config
        # 强制 inplace=False：避免 dual-stream 前向中 hidden_states 的写后读竞争
        self.moe_runner_config.inplace = False
        # 后续初始化逻辑保持不变
        self.dispatcher = base_layer.dispatcher
        # ...

```

### `test/registered/unit/lora/test_lora_moe_inplace_unit.py`

新增回归测试，覆盖 triton fallback 和 marlin 两种 runner core，验证构造函数强制 inplace=False。

```python
# test/registered/unit/lora/test_lora_moe_inplace_unit.py
class FusedMoEWithLoRAInplaceTest(unittest.TestCase):
    def _construct(self, quant_method=None):
        from sglang.srt.lora.layers import FusedMoEWithLoRA
        base_layer = _make_base_layer(quant_method)  # inplace=True
        lora_backend = types.SimpleNamespace()
        self.assertTrue(base_layer.moe_runner_config.inplace)
        layer = FusedMoEWithLoRA(base_layer, lora_backend)
        return layer, base_layer

    def test_constructor_forces_inplace_off(self):
        layer, base_layer = self._construct()
        self.assertIs(layer.moe_runner_config, base_layer.moe_runner_config)
        self.assertFalse(layer.moe_runner_config.inplace)

    def test_marlin_lora_runner_core_sees_non_inplace_config(self):
        # 模拟 marlin runner 后端
        with mock.patch("sglang.srt.layers.moe.utils.get_moe_runner_backend",
                        return_value=MoeRunnerBackend.MARLIN):
            layer, base_layer = self._construct(
                quant_method=mock.MagicMock(spec=CompressedTensorsFusedMoEMethod))
        core = getattr(layer._lora_runner, "runner_core", None)
        self.assertIsNotNone(core)
        core_config = getattr(core, "config", None) or getattr(core, "runner_config", None)
        self.assertIs(core_config, base_layer.moe_runner_config)
        self.assertFalse(core_config.inplace)

```

# 评论区精华

作者在 issue 评论中提供了确定性最小复现方法（单 GPU、无 serving 栈），复现 `forward_normal_dual_stream` 的 stream 边缘并与 eager 参考对比，证实 `inplace=True` 时满足 analytic 写后读。review 仅由 yushengsu-thu 审批通过，无额外讨论。

- 根因分析与确定性复现 (correctness): 确认 `inplace=True` 导致写后读竞争，修复方式为强制 `inplace=False`。

# 风险与影响

- 风险：影响范围窄，仅 LoRA 场景；通过强制 `inplace=False`（分配新 buffer）而非复用 `hidden_states`，可能带来轻微额外显存开销，但 PR body 中验证了 decode 吞吐和 tpot 不变。非 LoRA 路径已有独立分配，不受影响。测试覆盖两种 runner core，回归风险低。
- 影响：对使用 LoRA 且模型包含 dual-stream MoE（如 DeepseekV2）的用户意义重大，消除随机乱码；非 LoRA 用户无影响。修复本身一行代码，易于审查。
- 风险标记：CUDA graph 竞争 , LoRA 专用修复

# 关联脉络

- 暂无明显关联 PR