ACL 图#

概述#

ACL 图是 Ascend 对 vLLM 静态图执行的实现。上游 vLLM 和 PyTorch 文档已描述通用图模型,包括 CUDAGraphMode、运行时调度、批处理描述符、分桶与填充,以及完整图和分段图的定义。本文档聚焦于 vllm-ascend 中 Ascend 特有的内容:平台集成点、ACL 图捕获引入的额外约束,以及在重放期间保持注意力参数正确的机制。

在 Ascend 上,设计目标与上游静态图执行相同:减少中小运行时形状的主机启动开销。实现边界不同。vLLM 提供通用调度路径,而 vllm-ascend 提供 ACL 图重放所需的平台包装器、捕获大小裁剪和注意力特定更新逻辑。

前置条件与参考#

本文档有意不重新解释上游主题,如图模式选择、调度器行为、批处理描述符构建、捕获分桶、填充策略,或完整图与分段执行的通用含义。

ACL 图如何融入 vLLM#

vLLM 拥有通用静态图流程。在 Ascend 上,NPUPlatform.get_static_graph_wrapper_cls() 返回 vllm_ascend.compilation.acl_graph.ACLGraphWrapper,这是 vLLM 启用静态图模式时使用的平台特定包装器。

ACLGraphWrapper 负责:

  • 从前向上下文中读取运行时模式和 batch_descriptor

  • 决定是立即执行、捕获新的 ACL 图,还是重放缓存的 ACL 图,

  • 按批处理描述符缓存图条目,

  • 保留 Ascend 后端所需的图池和重放记账信息。

包装器不定义上游调度策略。它假设运行时模式和批处理描述符已由 vLLM 正确选择,然后对该具体运行时形状应用 Ascend 捕获或重放。

捕获大小与分桶#

vLLM 图重放需要稳定的运行时形状,因此 vLLM 不会尝试捕获所有可能的批处理形状。相反,它准备一组有限的捕获大小,并将运行时批处理调度到最近的支持大小。如果运行时批处理大于配置的最大捕获大小,则跳过图模式并回退到立即执行模式。

默认情况下,vLLM 构建的捕获大小为:

  • 124

  • 82558 的倍数

  • 256max_cudagraph_capture_size16 的倍数

概念上,默认列表如下:

[1, 2, 4, 8, 16, 24, 32, ..., 248, 256, 272, 288, ...]

小批处理大小的小步长减少了延迟最敏感区域的填充开销,而大批处理大小的较大步长则保持捕获图的数量在可控范围内。

在 Ascend 上,这个通用的上游分桶策略仍然是起点,但最终的捕获大小可能会因平台特定约束而进一步减少:

  • 序列并行过滤可能移除不支持的大小,

  • 流预算裁剪可能减少可捕获的大小数量,

  • 某些运行时模式可能在捕获开始前被规范化。

Ascend 特定设计约束#

流预算限制捕获广度#

与 CUDA Graph 不同,ACL 图捕获受流资源限制。当前实现将图计数视为流预算问题,并在 vllm_ascend.utils.update_aclgraph_sizes() 中相应裁剪捕获大小。裁剪逻辑从配置的捕获大小开始,根据模型深度和通信结构估算每图资源成本,并在请求范围超出支持预算时采样更小的代表性大小集。

当前实现使用约 1800 的实际最大图计数预算,低于设备流限制,并进一步减少通信密集型情况(如上下文并行执行)的预算。分段模式更受限制,因为每个捕获的段独立消耗资源,大约每层一个图。

通信执行模式也很重要。update_aclgraph_sizes() 根据 HCCL_OP_EXPANSION_MODE 使用不同的公式。实践中,HCCL_OP_EXPANSION_MODE=AIV 可以增加支持的捕获大小数量,而默认的通信展开路径更严格,减少了支持的运行时形状范围。

平台模式规范化比通用上游行为更严格#

Ascend 当前在 vllm_ascend.platform.NPUPlatform.check_and_update_config() 中收窄了一些通用上游模式。

  • 编码器-解码器模型被强制使用 PIECEWISE

  • use_inductor 对 ACL 图路径禁用。

  • 启用 ACL 图时,ASCEND_LAUNCH_BLOCKING=1 被拒绝。

  • Xlite 图模式可以禁用 ACL 图完整模式或回退到 FULL_DECODE_ONLY,具体取决于配置。

这些检查记录了当前 Ascend 后端可以安全执行的上游图行为子集。其中一些是长期平台约束,而另一些在当前实现中显然是过渡性的。

关键 Ascend 特定机制#

完整图重放的主机端注意力参数更新#

Ascend 上的完整图重放有一个上游通用文档未详细覆盖的额外问题:即使整体图是静态的,某些注意力算子也需要运行时元数据更新。Ascend 实现通过将图捕获与主机端任务参数更新分离来处理此问题。

流程如下:

  1. 在捕获期间,注意力后端记录每图任务句柄、事件、工作空间,以及必须刷新的张量或元数据的弱引用。

  2. 在重放之前,update_full_graph_params() 调用后端特定的 update_graph_params() 实现。

  3. 该后端在更新流上运行参数刷新,在底层注意力算子启动周围使用 torch.npu.graph_task_update_begin(...)torch.npu.graph_task_update_end(...)

  4. torch.npu.ExternalEvent 对象用于强制主机端更新流和重放流之间的顺序。

此机制在注意力后端中实现,例如:

  • vllm_ascend/attention/attention_v1.py

  • vllm_ascend/attention/mla_v1.py

  • vllm_ascend/attention/context_parallel/attention_cp.py

  • vllm_ascend/attention/context_parallel/mla_cp.py

重要的设计点是,Ascend 完整图支持依赖于后端提供的 update_graph_params() 钩子。没有这个钩子,仅靠捕获不足以重放正确的注意力状态。

重放顺序与同步#

ACLGraphWrapper 在通用路径的重放前同步当前流,以确保主机侧参数更新与将要使用它们的图执行保持对齐。这在异步调度或多线程执行中尤为重要。

如果顺序未得到保持,迭代 i 的参数更新可能被迭代 i-1 的重放观察到,或者迭代 i 的重放可能在它自身的参数更新完成之前就开始。实际上,这意味着注意力算子可能在运行时元数据不匹配的情况下运行,这可能导致错误结果、精度问题甚至挂起。代码为主流的完整图 eagle 情况保留了一条更窄的路径,但通用的设计假设是相同的:重放不得超越待处理的参数更新工作。

Ascend 上的完整图与分段图#

上游文档已经在语义上定义了完整图和分段图。在 Ascend 上,实际差异由后端支持和资源成本驱动。

分段模式#

分段模式是保守路径。它依赖通用的 vLLM 拆分执行策略,然后将 ACL 图捕获应用于编译路径选择的非注意力片段。在 Ascend 上,此模式是目前更广泛支持的选项,但它也对流压力最敏感,因为捕获的图数量随模型深度而增加。

完整图模式#

完整图模式是更注重性能的路径,前提是注意力后端能够通过 update_graph_params() 支持运行时参数修补。在 Ascend 上,完整图支持与那些注意力特定的更新钩子、工作空间缓存和重放顺序保证紧密相关。

诊断与操作说明#

  • 确认图模式是否激活的最简单方法是启用 cudagraph 指标并保持日志统计开启。在 CLI 使用中,使用 --cudagraph-metrics 且不传递 --disable-log-stats。在 Python 使用中,设置 cudagraph_metrics=Truedisable_log_stats=False。然后检查输出的指标和日志。

  • 性能分析也可以确认重放是否发生,开发者可以在本地调试时在重放前添加临时打印,但这些都是次要方法,此处不展开说明。

  • update_aclgraph_sizes() 是基于流预算驱动的捕获大小调整的主要实现点。

  • 在调试模式下,ACLGraphWrapper 断言重放使用与捕获期间记录的相同的张量地址。

  • 在当前实现中,ASCEND_LAUNCH_BLOCKING=1 与 ACL 图启用不兼容。

  • 对于图执行内部的调试,代码库还在 vllm_ascend.utils 中提供了图感知的打印辅助工具,但这些是开发者诊断工具,而非执行设计的一部分。