PA workspace 跨层复用

通用 GQA/PA性能优化PR #15579已合入 main作者 solin28825 · 2026-09-04 · 改动 1 文件 +42/-10

一、需求背景

图模式下走 _npu_paged_attention(PA)路径时,每步 decode 前要更新图参数 (update_paged_attention_graph_param,attention/attention_v1.py)。优化前的循环里, 每一层都调用一次 torch_npu._npu_paged_attention_get_workspace:

同一 decode 步内,所有层的形状和 seq_lens 完全相同——28 次查询查的是同一个结果, 纯粹是重复劳动,还把 host 侧 ACL API 时间线撑爆。

二、相关流程

图1 · 改动前:每层一次 get_workspace(28 层 = 28 次分配 + Setup 往返)
sequenceDiagram participant H as Host participant D as NPU loop 每层 ×28 H->>D: get_workspace(36-54MB 分配 + Setup 往返) H->>D: graph_task_update_begin + PA 执行 end Note over H,D: Setup 时间 ≈ 2× Execute 时间(实测比 2.000)

改动后:workspace 查询提到循环外,每轮更新只查一次、跨层复用; 只有当尺寸相关输入变化(seq_lens 张量身份 (data_ptr, shape) 及各形状/dtype 组成 的 key 不匹配)才重新查询。这与 FIA 路径已有的 graph_params.workspaces[num_tokens] 复用形态对齐。

图2 · 改动后:每轮一次查询,跨层复用(key 不匹配自动回退重查)
sequenceDiagram participant H as Host participant D as NPU H->>D: get_workspace(本轮仅 1 次) loop 每层 ×28 H->>H: ws_key 匹配?→ 复用 workspace H->>D: graph_task_update_begin + PA 执行 end Note over H,D: Setup/Execute 比例 2.000 → 1.036

三、需求改动

单文件改动(attention/attention_v1.py,+42/-10)。三点设计值得注意:

设计点说明
ws_key 覆盖全部尺寸输入(data_ptr, shape) + query/key_cache/value_cache 的 shape 与 dtype + block_table.shape——异构层配置只是触发重查,绝不会复用错误尺寸
只做轮内复用,刻意不做跨步workspace 大小还依赖 seq_lens 的数值(实测 len≤80 时 36MB、len≤400 时 54MB,随 decode 增长)——上一步的缓存可能偏小,复用有风险
保守回退任一层 key 不匹配 → 立即走一次全新 get_workspace,正确性优先
代码 · 改动前(attention_v1.py:540,循环内每层查询)
with torch.npu.stream(update_stream):  # 图参数更新(每步一次,内含逐层循环)
    for key, param, handle, event in zip(  # ↳逐层循环:28 层模型每步 28 轮(问题所在)
        forward_context.attn_metadata,
        graph_params.attn_params[num_tokens],
        graph_params.handles[num_tokens],
        graph_params.events[num_tokens],
    ):
        (
            query,
            key_cache,
            value_cache,
            num_kv_heads,
            num_heads,
            scale,
            block_table,
            seq_lens,
            output,
        ) = param
        seq_lens = forward_context.attn_metadata[key].seq_lens  # 同一步内所有层共享同一 seq_lens(跨层复用的前提)

        workspace = torch_npu._npu_paged_attention_get_workspace(  # ⚡每层都查一次:36-54MB 分配 + Setup 往返(问题所在)
            query=query,
            key_cache=key_cache,
            value_cache=value_cache,
            num_kv_heads=num_kv_heads,
            num_heads=num_heads,
            scale_value=scale,
            block_table=block_table,
            context_lens=seq_lens,
            out=output,
        )
        torch.npu.graph_task_update_begin(update_stream, handle)  # 图任务重放绑定 + PA 执行
        torch_npu._npu_paged_attention(
            query=query,
改动 · diff(commit 827a11d3,step_ws_key + 跨层复用)
step_ws_key = None  # ⚡本轮 workspace 缓存键(跨层复用的核心)
workspace = None
for key, param, handle, event in zip(
    forward_context.attn_metadata,
    graph_params.attn_params[num_tokens],
    ) = param
    seq_lens = forward_context.attn_metadata[key].seq_lens

    # The key covers every size-relevant input, so models
    # with heterogeneous layer configs simply trigger a
    # re-query instead of reusing a wrong-sized workspace.
    ws_key = (  # 键覆盖全部尺寸相关输入:任何差异都触发重查(保守设计)
        seq_lens.data_ptr(),
        tuple(seq_lens.shape),
        query.shape,
        query.dtype,
        key_cache.shape,
        key_cache.dtype,
        value_cache.shape,
        value_cache.dtype,
        block_table.shape if block_table is not None else None,
        block_table.dtype if block_table is not None else None,
        output.shape,
        output.dtype,
        num_kv_heads,
        num_heads,
        scale,
    )
    if step_ws_key != ws_key:  # 键不匹配才重新查询(异构层配置自动回退)
        workspace = torch_npu._npu_paged_attention_get_workspace(  # 每轮最多查询一次(原为每层一次)
            query=query,
            key_cache=key_cache,
            value_cache=value_cache,
            num_kv_heads=num_kv_heads,
            num_heads=num_heads,
            scale_value=scale,
            block_table=block_table,
            context_lens=seq_lens,
            out=output,
        )
        step_ws_key = ws_key  # 记录本轮键:后续层直接复用
    torch.npu.graph_task_update_begin(update_stream, handle)
    torch_npu._npu_paged_attention(

效果(910B 实测,vLLM v0.26.0 / TP=1 / FULL_DECODE_ONLY / c=32):

指标改动前改动后
get_workspace Setup/Execute 比2.000(22232/11116)1.036(14355/13860)
update_graph_params 均值2.4-2.95 ms1.65-1.67 ms(每步省 1.1-1.3ms)
Host ACL API 总时长(5.1s 窗口)1335 ms1040 ms(-22%)
QPS c=1 / c=32 / c=641.27 / 16.73 / 21.491.35 / 17.13 / 21.90(+5.9%/+2.4%/+1.9%)

功能输出不变(全功能套件 md5 一致)。

四、相关链接

首页