NPU 上的多流并发执行:AIR 异步调度模型解析

前言

随着人工智能应用的日益复杂化和实时性要求的提高,如何在(Ascend)AI 处理器上高效地实现多路数据流的并发执行,成为了高性能异构计算领域的核心挑战之一。传统的同步调度模型往往受限于单一任务的完成时间,无法充分利用 NPU 的并行计算能力,导致资源利用率低下。

为了解决这一问题,计算架构(CANN)引入了异步调度模型(Asynchronous Execution Model),并在其核心组件中得到了体现。本文将深入剖析基于 CANN 框架的异步调度机制,特别是聚焦于 AIR(Ascend Intermediate Representation) 框架在实现多流并发执行中的关键作用,并结合 CANN 组织 上的开源实践,探讨其技术原理与优化策略。

核心技术原理:异步调度的必要性与 AIR 框架

在 NPU 上进行推理或训练任务时,通常涉及多个独立的输入流(例如,来自不同传感器的视频流、多个用户的请求等)。如果采用阻塞式的同步调用,一个流的等待会阻塞其他流的执行,无法实现真正的并发。

异步调度的核心思想是:当一个计算任务提交后,主机(Host CPU)不等待 NPU 上的计算完成,而是立即返回,继续执行后续的 CPU 逻辑或提交下一个 NPU 任务。NPU 上的计算则在后台独立运行,并通过回调或事件通知机制告知主机任务完成。

AIR 框架在这一过程中扮演了至关重要的角色。AIR 是 CANN 统一的中间表示层和编译器前端,它负责将上层框架(如 TensorFlow、PyTorch)的模型转换为 NPU 可执行的指令集。在异步调度的语境下,AIR 的作用体现在:

  1. 图的分解与依赖分析: AIR 能够对计算图进行精细化的分析,识别出可以并行执行的子图或算子。
  2. 流的抽象与管理: AIR 内部机制需要能够将不同的输入流映射到 NPU 资源的不同执行队列或上下文,确保它们之间互不干扰。
  3. 异步指令的生成: 编译器需要生成包含同步点(Synchronization Primitives)的指令流,而不是简单的顺序执行流。这些同步点(如 Wait/Signal)用于管理资源依赖和任务完成通知。

通过 air 仓库 的源码和文档可以观察到,AIR 编译器在优化阶段会进行数据流分析(Data Flow Analysis)控制流分析(Control Flow Analysis),以最大化地暴露并行性。

代码/架构分析:AIR 异步调度机制的实现

在 CANN 的运行时(Runtime)层面,异步调度的实现依赖于以下几个关键组件的协同工作:

1. Stream 与上下文管理

NPU 上的并发执行是通过**流(Stream)**来实现的。在 CANN 中,一个 Stream 代表了一组按序执行的命令队列。异步调度的关键在于,系统可以同时激活多个 Stream,每个 Stream 负责处理一个独立的数据流任务。

AIR 编译生成的 IR(Intermediate Representation)会明确标记哪些操作属于哪个逻辑流。在下发到 NPU 驱动层时,这些操作会被精确地分配到对应的硬件上下文和命令队列中。

2. 异步 API 调用

与同步 API(如 aclopExecute 的阻塞版本)不同,异步执行通常使用带有回调或事件通知的接口。例如,在 acl.h 接口中,我们通常会看到:

  • aclrtCreateStream() 创建一个独立的执行流。
  • aclopExecuteAsync() 提交算子执行请求,但不等待结果。
  • aclrtRecordEvent()aclrtSynchronizeStream() 用于在不同的流之间建立依赖关系,或者在特定点等待所有流完成。

AIR 编译后的二进制文件(通常是 .om 文件)内部包含了执行图及其依赖关系。当运行时引擎加载这个 .om 文件时,它会根据图中的控制流和数据流信息,动态地管理多个 Stream 的调度,以实现最大化的并发度。

3. 依赖图与同步原语

异步调度的挑战在于如何正确处理数据依赖。如果 Stream B 依赖于 Stream A 的计算结果,那么 Stream B 的执行必须在 Stream A 完成后才能开始。

AIR 编译器会识别出这种依赖关系,并在生成的底层指令中嵌入同步原语(Synchronization Primitives),例如内存屏障或事件等待/通知机制。当 NPU 硬件执行到特定的同步点时,它会检查依赖是否满足。如果满足,则继续执行;如果不满足,则暂停当前流的执行,直到依赖的事件被触发。

这种基于事件和流的调度机制,使得 NPU 能够像 CPU 的乱序执行单元一样,在满足依赖的前提下,并行处理来自不同输入流的任务。

性能优化实践:最大化多流并发收益

要真正发挥异步调度的优势,仅仅实现并发是不够的,还需要精细的优化:

  1. 细粒度流划分: 避免将整个推理任务视为一个大流。如果模型结构允许,应尝试将模型的不同阶段或不同输入分支划分为独立的子流。AIR 编译器在这方面提供了自动化的能力,但用户也可以通过模型重构或自定义算子(Operator)来引导更细粒度的流划分。
  2. 最小化同步开销: 同步点是异步调度的“刹车”。过多的同步操作会退化为同步执行。优化目标是让同步点只出现在真正必要的数据依赖处。AIR 在 IR 转换和优化阶段会努力减少不必要的同步指令。
  3. Host-Device 协同优化: 异步执行要求 Host CPU 能够快速地准备和提交下一个任务,而不需要等待 NPU 结果。这要求 Host 端的内存管理和数据预处理逻辑也应尽量异步化,避免 CPU 成为瓶颈。

总结

NPU 上的多流并发执行是实现高吞吐率和低延迟推理的关键。通过深入研究 CANN 框架,特别是 AIR 编译器的设计,我们可以看到异步调度模型是建立在精细的流管理、依赖分析和同步原语之上的。

AIR 框架作为中间表示层,有效地将上层应用的需求转化为 NPU 可执行的、高度并行的异步指令流。开发者应充分理解 CANN Runtime 提供的异步 API,并结合 AIR 的优化能力,设计出能够最大化 NPU 并行潜力的计算架构。要持续深入研究这一领域,推荐关注 CANN 组织air 仓库 的最新进展和技术文档,以掌握最前沿的异构计算调度技术。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐