Prhub

#32115 [MLX] Size request capacity by attention DP

原始 PR 作者 wirybeaver 合并时间 2026-07-30 09:18 文件变更 2 提交数 4 评论 12 代码增减 +148 / -10

执行摘要

MLX 请求容量按 attention DP 大小分配,修复纯 DP 副本容量不足

作为 #32101 和 #32102(Gemma 4 Apple Silicon MVP)的前置条件。MLX stub 之前将 max_running_requests 除以系统 dp_size,在启用 DP attention 时正确(attn_dp_size == dp_size),但在纯 DP 场景下会导致每个 replica 分配到的容量不足,因为 attn_dp_size == 1 时每个 replica 应保留全量配置限制。需要与 KVCacheConfigurator.resolve_max_num_reqs() 的行为一致,即按 ps.attn_dp_size 划分请求容量。

建议:MLX 后端开发者和 CI 维护者审查此 PR,确保后续 #32101、#32102 可以无缝集成。测试覆盖较全,可放心合并。注意 @noob-se7en 正在重构 model_runner_stub,未来可能再次调整此逻辑。

讨论亮点

Review 中 yeahdongcn 提出了几项关键改进:要求同步分割 max_mamba_cache_size(已实现);建议错误消息保持全局 CLI 单位(已实现);要求测试通过 initialize() 初始化且使用 CustomTestCase(已实现)。jlee5814 也指出了 fixture 设置方式可能隐藏错误。整体上讨论聚焦于保持与基类解析器行为一致和测试充分性。

实现拆解

  1. 修改 _resolve_max_running_requests 方法:将请求容量划分依据从 self.dp_size 改为 self.ps.attn_dp_size。纯 DP(attn_dp_size=1)时每个 replica 保留完整配置限制;DP attention(attn_dp_size>1)时按 attention-DP 所有者数量分割。
  2. 新增 _explicit_aux_state_size_per_worker 方法:将全局 max_mamba_cache_sizeps.attn_dp_size 分割为 per-worker 值,并在 _resolve_max_running_requests 中替代直接使用全局值。
  3. 更新 initialize() 方法:在构造 MlxAuxiliaryStateReqToTokenPool 时使用 per-worker 辅助状态容量。
  4. 改进错误诊断:保留全局 max_mamba_cache_size 用于错误消息,推荐最小全局值为 ratio * ps.attn_dp_size,并显示 per-worker 辅助状态 cap。
  5. 新增测试文件 test_attn_dp_request_capacity.py,注册 MLX 和 CPU CI,包含 4 个测试用例:纯 DP 保留全量、DP attention 分割、混合 attention-DP 分割辅助状态、辅助状态错误报告使用全局单位。
文件 模块 状态 重要度
python/sglang/srt/hardware_backend/mlx/model_runner_stub.py MLX 后端 modified 7.48
test/registered/unit/hardware_backend/mlx/test_attn_dp_request_capacity.py MLX 测试 added 7.53

关键符号

_explicit_aux_state_size_per_worker _resolve_max_running_requests initialize

关键源码片段

python/sglang/srt/hardware_backend/mlx/model_runner_stub.py core-logic

核心逻辑变更:修改请求容量和辅助状态容量的分割方式,按 attention DP 大小而非全局 DP 大小划分

def _explicit_aux_state_size_per_worker(self) -> int | None:
    '''Return the explicit auxiliary-state cap for this attention-DP owner.'''
    aux_state_size = self.server_args.max_mamba_cache_size
    if aux_state_size is None:
        return None
    # 按 attention-DP 大小分割全局容量,使每个所有者获得相等份额
    return aux_state_size // self.ps.attn_dp_sizedef _resolve_max_running_requests(self) -> int:
    '''Concurrency cap handed to the scheduler.    核心变更:将 `requested` 除以 `self.ps.attn_dp_size` 而非 `self.dp_size`,
    使得纯 DP(attn_dp_size=1)时保留完整限制,DP attention 时按注意力 DP 所有者分割。
    '''
    capacity_cap = self.max_total_num_tokens // 2
    requested = self.server_args.max_running_requests
    if requested is None:
        requested_per_worker = None
        resolved = min(capacity_cap, 4096)
    else:
        requested_per_worker = requested // self.ps.attn_dp_size
        resolved = min(requested_per_worker, capacity_cap)
​
    aux_state_size = self._explicit_aux_state_size_per_worker()
    if (
        mambaish_config(self.model_config) is not None
        and aux_state_size is not None
    ):
        ratio = self._aux_state_slots_per_request()
        resolved = min(resolved, aux_state_size // ratio)
        if resolved <= 0:
            global_aux_state_size = self.server_args.max_mamba_cache_size
            min_global = ratio * self.ps.attn_dp_size
            raise RuntimeError(
                'MLX auxiliary-state cache is too small to serve any '
                'requests: max_mamba_cache_size={} '
                'backs only {} concurrent requests '
                'per attention-DP worker (per-worker cap={}, '
                '{} slots per request). Increase --max-mamba-cache-size '
                'to at least {}.'.format(
                    global_aux_state_size,
                    aux_state_size // ratio,
                    aux_state_size,
                    ratio,
                    min_global,
                )
            )
    if requested_per_worker is not None and resolved < requested_per_worker:
        logger.warning(
            'max_running_requests reduced from %d to %d '
            '(per attention-DP worker) due to KV cache or auxiliary-state capacity.',
            requested_per_worker, resolved,
        )
    return resolved

评论区精华

同步分割辅助状态容量 正确性

yeahdongcn 指出 requested 已转换为 per-owner 值,但 max_mamba_cache_size 仍是未分割的全局值,会导致每个 owner 得到相同的全局容量。要求同步分割。

结论:作者在 9c7e71c6e 中实现分割,使用 _explicit_aux_state_size_per_worker 方法。 · 已解决

错误消息使用全局 CLI 单位 正确性

yeahdongcn 指出分割后的 per-worker 值不应替代全局 cap 在错误消息中,用户设置的 --max-mamba-cache-size 是全局值,错误消息应保持全局单位并显示 per-worker cap,推荐最小全局值。

结论:作者在 ebb4e9cb6 中实现,错误消息使用全局 max_mamba_cache_size,显示 per-worker cap,推荐 ratio * ps.attn_dp_size。 · 已解决

测试 fixture 应通过 initialize() 初始化 测试

jlee5814 担心直接设置 stub.dp_size 会隐藏缺失属性,类似 #30547 的错误。建议使用 ParallelState.trivial 并通过 initialize() 初始化。

结论:作者重写测试 fixture 使用 ParallelState.trivial 并调用 initialize(),添加了 hybrid 测试用例。 · 已解决

测试类应继承 CustomTestCase style

yeahdongcn 指出注册单元测试策略要求使用 CustomTestCase 以启用 CI 重试行为。

结论:作者将继承从 unittest.TestCase 改为 sglang.test.test_utils.CustomTestCase。 · 已解决

风险与影响

风险:仅影响 MLX 后端;容量计算变更可能改变现有部署中请求限制的实际值,需确保与基类解析器行为一致;错误消息变更可能影响用户排查;对非 MLX 后端无影响。测试覆盖了主要场景,但实际部署验证仍有必要。

影响:MLX 后端用户(Apple Silicon);纯 DP 部署现在能正确分配请求容量,DP attention 部署行为不变;hybrid 模型(如 Mamba)的辅助状态容量正确分割;错误消息提供更准确的全局建议值,方便用户调整参数。

MLX 后端容量分配变更 测试覆盖新行为

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论