PyPTO Pro 抽象硬件架构:AIV/AIC 计算资源、存储层级与数据同步全解析
【免费下载链接】pyptoPyPTO(发音: pai p-t-o):Parallel Tensor/Tile Operation编程范式。项目地址: https://gitcode.com/cann/pypto
本文基于 PyPTO Pro 的 SIMD 编程范式,系统讲解 AI Core 的抽象硬件视图:AIV 的 Vector 资源与 AIC 的 Cube 资源、GM 与片上存储(UB、L1 Buffer、L0A/L0B/L0C Buffer)的层级关系,以及 Scalar 指令流、搬运/计算数据流与流水间同步的底层机制。读完本文,你将掌握选择 Tile 计算、Reg 计算或 Cube 计算的依据,并能据此正确组织数据搬运与同步,写出结构合理的 SIMD Kernel。
从抽象硬件架构理解三种 SIMD 计算方式
PyPTO Pro 的 SIMD(Single Instruction Multiple Data,单指令多数据)编程以数据块为基本编程对象,一条指令同时对多个同构数据元素执行相同操作。其底层计算资源分为两类:
- AIV(Vector Core)上的 Vector 资源:执行矢量运算,是 Tile 矢量计算和 Reg 计算的执行资源;
- AIC(Cube Core)上的 Cube 资源:执行矩阵运算,是 Cube 矩阵计算的执行资源。
理解这两类计算资源及其数据通路,是选择 Tile计算、Reg计算 或 Cube计算 的前提,也是正确组织数据搬运与同步的基础。SIMT 的线程执行资源属于另一套编程范式,参见 SIMT抽象硬件架构。
AI Core 的相关硬件可以从计算单元、存储单元和搬运单元三个方面理解。下图展示了 AIC、AIV 及其指令流和主要数据流的关系:
图中黑色实线表示主要数据流,橙色虚线表示指令流。图中的 SIMT 资源仅用于说明其在 AIV 中的位置,不属于本文讨论范围。
计算单元:Scalar、Vector 与 Cube 的分工
| 单元 | 主要作用 | PyPTO Pro 计算方式 |
|---|---|---|
| Scalar | 处理地址计算、循环控制等标量逻辑,向计算和搬运流水发射指令 | Kernel 中的控制逻辑 |
| AIV 上的 Vector 单元 | 执行矢量运算 | Tile计算、Reg计算 |
| AIC 上的 Cube 单元 | 执行矩阵运算 | Cube计算 |
三种计算方式在数据载体上存在本质差异:
- Tile 计算以 UB 中的 Tile 为计算数据载体,通过
pypto_pro.language.section_vector()执行域完成批量运算,适合逐元素、归约、数据类型转换和数据重排等通用矢量场景; - Reg 计算将 UB 中的数据加载到 Vector Register File,中间结果可以保留在寄存器中,适合计算链较长、需要减少 UB 往返访问的高性能场景;
- Cube 计算从 L0A Buffer 和 L0B Buffer 读取矩阵操作数,将累加结果写入 L0C Buffer,通过一条矩阵指令并行完成一个矩阵分块的乘加运算。
各方式的接口和使用约束见对应的编程章节,本文从硬件视角解释其设计动机。
存储单元与搬运单元:数据在存储层级中的流转
GM 位于 AI Core 之外,用于保存 Kernel 输入、输出及 Workspace。AIV 和 AIC 各有片上存储,计算前需要将数据搬入相应的存储层级。不同执行侧的主要片上存储与典型数据路径如下:
| 执行侧 | 主要片上存储 | 典型数据路径 |
|---|---|---|
| AIV Tile 计算 | UB | GM → UB → Vector 单元 → UB → GM |
| AIV Reg 计算 | UB、Vector Register File | GM → UB → Vector Register File → UB → GM |
| AIC Cube 计算 | L1 Buffer、L0A Buffer、L0B Buffer、L0C Buffer | GM → L1 Buffer → L0A Buffer/L0B Buffer → Cube 单元 → L0C Buffer → GM |
表中的路径表示一次计算所涉及的主要存储层级,不表示所有搬运和计算必须串行完成。Reg 计算的寄存器数据路径及硬件单元详见 Reg计算;AIC 各 Buffer 的作用、布局和搬运路径详见 Cube计算。
搬运单元:MTE 系列与 DMA
MTE(Memory Transfer Engine)等搬运单元负责 GM 与片上 Buffer 以及不同片上 Buffer 之间的数据流转,不同搬运路径由不同的搬运流水承担:
- AIV 侧:GM 与 UB 之间的搬运主要使用MTE2(GM → UB)和MTE3(UB → GM);Reg 计算还使用DMA 单元在 UB 与 Vector Register File 之间搬运数据;
- AIC 侧:矩阵数据通常经MTE2进入 L1 Buffer,再经MTE1进入 L0A Buffer、L0B Buffer;结果搬出由FIX流水负责。
从源码实现可以印证这种流水划分:framework/src/codegen/codegen_common.h中定义了完整的流水枚举PIPE_MTE1、PIPE_MTE2、PIPE_MTE3、PIPE_V(Vector)、PIPE_M(矩阵)、PIPE_FIX、PIPE_S(Scalar)等,并在PIPE_ID映射表中与"PIPE_MTE1"、"PIPE_MTE2"、"PIPE_MTE3"等字符串一一对应,代码生成阶段正是依据这些流水标识编排指令与同步。
具体接口及支持的搬运路径以 Tile创建和操作、Reg计算 和 Cube计算 为准。
存储层级的 PyPTO Pro 表达
PyPTO Pro 用不同的数据对象表示数据在存储层级中的位置,这一点在 SIMD编程范式 中有完整的对象-位置对应表:
| 数据对象 | 数据位置 | 作用 |
|---|---|---|
| Tensor | GM | 描述 Kernel 输入、输出或 Workspace 中的多维数据视图 |
| Tile | UB、L1 Buffer、L0A Buffer、L0B Buffer 和 L0C Buffer 等片上存储 | 表示当前分块的数据,是 Tile 矢量计算和 Cube 计算的操作数 |
| TileGroup | 与 Tile 相同 | 管理一组轮转 Tile,用于单缓冲、双缓冲和 N 缓冲 |
| RegTensor、MaskReg | Vector Register File | 保存 Reg 计算的输入、中间结果和输出 |
Tensor 表示全局数据,Tile 表示当前 AI Core 处理的局部数据块。开发者通过 Tiling 将大 Tensor 划分成多个 Tile,再由不同核和不同循环迭代处理这些 Tile。target_memory决定 Tile 绑定的物理缓冲区和可使用的数据路径,Tile创建和操作 中的MemorySpace枚举与 Buffer 的对应关系为:
| pypto_pro.language.MemorySpace | 物理缓冲区 | 典型用途 |
|---|---|---|
| Vec | UB | 矢量数据的输入、输出和中间结果 |
| Mat | L1 Buffer | GM 与 L0A Buffer/L0B Buffer 之间的矩阵暂存 |
| Left | L0A Buffer | 矩阵乘左操作数 |
| Right | L0B Buffer | 矩阵乘右操作数 |
| Acc | L0C Buffer | 矩阵乘累加结果 |
| Bias | BiasTable Buffer | 矩阵乘的融合偏置 |
| Scaling | Fixpipe Buffer | FIX 数据通路的 per-channel 随路量化/反量化参数 |
| ScaleLeft | L0A_MX Buffer | MX 矩阵乘的左量化系数矩阵 |
| ScaleRight | L0B_MX Buffer | MX 矩阵乘的右量化系数矩阵 |
指令流、数据流与同步:源码顺序不等于硬件完成顺序
图 1 中的指令流与数据流含义不同:Scalar 将搬运和计算任务发射到相应流水;搬运单元和计算单元按各自的数据路径访问存储资源。不同流水可以异步执行,因此源码中的先后顺序不等同于硬件上的完成顺序——这是理解同步机制的核心前提。
当一条流水生产的数据要由另一条流水消费时,需要建立同步依赖。例如:
- Vector 计算读取 UB 数据之前,相关搬入(MTE2)必须完成;
- 将计算结果搬回 GM(MTE3)之前,相关 Vector 计算也必须完成。
需要强调的是,同步信号只约束执行顺序,不承担数据搬运。以 Cube 计算为例,四步操作分别对应 MTE2、MTE1、M、FIX 四条流水线,各自异步执行:
| 流水线 | 含义 | 典型操作 |
|---|---|---|
| MTE2 | GM → L1 Buffer 搬运 | pypto_pro.language.load/pypto_pro.language.load_tile |
| MTE1 | L1 Buffer → L0A Buffer/L0B Buffer 搬运 | pypto_pro.language.move |
| M | 矩阵计算 | pypto_pro.language.matmul/pypto_pro.language.matmul_acc |
| FIX | L0C Buffer → GM 搬运 | pypto_pro.language.store/pypto_pro.language.store_tile |
同步的两种组织方式
PyPTO Pro 提供两种依赖管理方式:
- TileGroup 与自动 mutex:通过
make_tile_group为每块 Tile 绑定mutex_id,配合@pypto_pro.language.jit(auto_mutex=True),框架根据 Tile 的使用关系和 mutex 元数据自动插入同步; - 显式同步接口:使用
pypto_pro.language.system.sync_src/pypto_pro.language.system.sync_dst成对指定生产流水与消费流水之间的依赖。
显式同步的典型用法是成对出现,sync_src由生产流水线 SET flag,sync_dst由消费流水线 WAIT flag,两者的set_pipe、wait_pipe和event_id必须一致。静态event_id取值范围为 [0, 7],动态整数 Scalar 的运行时数值也必须在该范围内;同一 ID 只能在上一次同步被消费后复用:
import pypto_pro.language as pl with pl.section_cube(): pl.load(a_l1, a, [0, 0]) pl.load(b_l1, b, [0, 0]) pl.system.sync_src( set_pipe=pl.PipeType.MTE2, wait_pipe=pl.PipeType.MTE1, event_id=0) pl.system.sync_dst( set_pipe=pl.PipeType.MTE2, wait_pipe=pl.PipeType.MTE1, event_id=0) pl.move(a_left, a_l1) pl.move(b_right, b_l1) pl.system.sync_src( set_pipe=pl.PipeType.MTE1, wait_pipe=pl.PipeType.M, event_id=1) pl.system.sync_dst( set_pipe=pl.PipeType.MTE1, wait_pipe=pl.PipeType.M, event_id=1) pl.matmul(acc, a_left, b_right) pl.system.sync_src( set_pipe=pl.PipeType.M, wait_pipe=pl.PipeType.FIX, event_id=2) pl.system.sync_dst( set_pipe=pl.PipeType.M, wait_pipe=pl.PipeType.FIX, event_id=2) pl.store(out, acc, [0, 0])多核并行与执行域映射
多个 AIC 或 AIV 可以并行处理不同数据分片。核数配置、核索引以及混合 Kernel 中 AIC 与 AIV 的映射关系,参见 SIMD编程范式 中的“多核SPMD与核内SIMD”和 多核Tiling切分。
PyPTO Pro SIMD Kernel 采用"外层多核 SPMD + 内层单核 SIMD"的两级并行方式:Host 侧通过block_dim配置核数;Kernel 内使用pypto_pro.language.get_block_num()取得实际生效核数,使用pypto_pro.language.get_block_idx()取得当前执行域的核索引:
import pypto_pro.language as pl num_cores = pl.get_block_num() core_id = pl.get_block_idx() # 第core_id个核以num_cores为步长处理数据块。 for tile_idx in pl.range(core_id, tile_num, num_cores): ...常用的跨步切分让各核处理的数据块数量最多相差 1,并避免由单个核串行遍历全部数据。block_dim的默认值、调用形式和不同 Kernel 类型下的含义参见 Kernel核函数。
按执行资源划分,SIMD Kernel 有三种类型:
| Kernel 类型 | 执行资源 | get_block_idx()的含义 |
|---|---|---|
| 纯 Vector Kernel | AIV | 当前 Vector 核的全局索引 |
| 纯 Cube Kernel | AIC | 当前 Cube 核的全局索引 |
| Cube/Vector 混合 Kernel | AIC 和 AIV | 在各自执行域中返回相应的全局核索引 |
混合 Kernel 采用 AIC:AIV 为 1:2 的映射:每个 AI Core 执行组包含一个 AIC 和两个 AIV。当block_num = pypto_pro.language.get_block_num()时,Cube 执行域实际使用block_num个 AIC,Vector 执行域实际使用2 * block_num个 AIV。
从硬件视角回看三种计算方式的数据流
Tile 矢量计算:GM → UB → Vector → UB → GM
Tile 计算以 UB 中的二维 Tile 为计算对象,在section_vector()执行域中完成。一个完整算子的典型数据链路为:
Tensor → 数据搬入 → 输入Tile → Tile计算 → 输出Tile → 数据搬出 → Tensor计算接口将结果写入预先准备的目的 Tile,不会创建或返回新的 Tile。例如:
import pypto_pro.language as pl with pl.section_vector(): pl.add(out_tile, lhs_tile, rhs_tile) pl.relu(out_tile, out_tile)同一 Vector 计算流水上的相邻操作按依赖顺序执行,不需要在操作之间插入跨 Pipe 同步;Tile 在 MTE2、Vector 和 MTE3 等不同流水之间传递时,仍需通过基于 TileGroup mutex 元数据的自动同步或显式同步保证读写顺序。尾块(实际有效范围小于 Tile 物理 shape)场景需要在数据搬入前用pl.set_validshape设置有效区域,使随后的 load、计算和 store 使用同一有效区,避免越界读写。完整尾块示例可参考 Tile计算 的"二维加法的四类尾块"一节。
Reg 计算:GM → UB → Register File → UB → GM
Register File 位于 UB 之上,不能直接从 GM 加载或直接写回 GM。Reg 计算将一段连续计算保留在寄存器中,仅在计算链入口和出口与 UB 交互,通过@pypto_pro.language.vector_function定义 VF 函数,在函数内使用vf.*接口表达寄存器加载、计算和存储:
| 数据路径 | PyPTO Pro 表达 |
|---|---|
| GM → UB | pypto_pro.language.load/pypto_pro.language.load_tile |
| UB → Register File | vf.load/vf.load_align/vf.load_unalign等 |
| Register File 内计算 | vf.add、vf.mul、vf.reduce_sum等 |
| Register File → UB | vf.store/vf.store_align/vf.store_unalign等 |
| UB → GM | pypto_pro.language.store/pypto_pro.language.store_tile |
参与 Reg 计算的硬件单元包括 Reg 矢量执行单元、DMA 单元(UB 与 Register File 之间搬运)和 Aux Scalar(VF 域内的地址、循环等标量计算)。GM ↔ UB 搬运与 Vector/VF 计算之间的跨 Pipe 依赖,由 TileGroup +auto_mutex=True自动管理,或使用sync_src/sync_dst手动管理。详见 Reg计算。
Cube 矩阵计算:GM → L1 → L0 → Cube → L0C → GM
Cube 矩阵计算的基本步骤为:创建 L1 Buffer、L0A Buffer、L0B Buffer 和 L0C Buffer → 将左、右矩阵从 GM 搬入 L1 Buffer → 将 L1 Buffer 数据搬入 L0A Buffer/L0B Buffer → 执行矩阵计算写入 L0C Buffer → 将结果写回 GM → 正确配置同步指令。这些步骤需要写在pypto_pro.language.section_cube()标记的 Cube 执行域中:
import pypto_pro.language as pl TILE_M = 128 TILE_K = 128 TILE_N = 128 # L1 Buffer使用双缓冲,L0 Buffer使用单缓冲,均由auto_mutex管理同步 a_l1 = pl.make_tile_group( type=pl.TileType(shape=[TILE_M, TILE_K], dtype=pl.DT_FP16, target_memory=pl.MemorySpace.Mat), addrs=0x00000, mutex_ids=[0, 1]) b_l1 = pl.make_tile_group( type=pl.TileType(shape=[TILE_K, TILE_N], dtype=pl.DT_FP16, target_memory=pl.MemorySpace.Mat), addrs=0x10000, mutex_ids=[2, 3]) a_left = pl.make_tile_group( type=pl.TileType(shape=[TILE_M, TILE_K], dtype=pl.DT_FP16, target_memory=pl.MemorySpace.Left), addrs=0x0000, mutex_ids=[4]) b_right = pl.make_tile_group( type=pl.TileType(shape=[TILE_K, TILE_N], dtype=pl.DT_FP16, target_memory=pl.MemorySpace.Right), addrs=0x0000, mutex_ids=[5]) acc = pl.make_tile_group( type=pl.TileType(shape=[TILE_M, TILE_N], dtype=pl.DT_FP32, target_memory=pl.MemorySpace.Acc), addrs=0x0000, mutex_ids=[6]) with pl.section_cube(): pl.load_tile(a_l1.next(), a, [0, 0]) pl.load_tile(b_l1.next(), b, [0, 0]) pl.move(a_left.current(), a_l1.current()) pl.move(b_right.current(), b_l1.current()) pl.matmul(acc.current(), a_left.current(), b_right.current()) pl.store_tile(out, acc.current(), [0, 0])Cube 计算以分形(Fractal)为基本计算和搬运单位,load在搬运过程中自动完成 ND→NZ 格式转换,move自动完成 NZ→ZN(L0B Buffer)转换,store自动完成 NZ→ND 转换。矩阵分形、片上地址、数据搬运和计算接口的完整规则参见 Cube计算。
总结:硬件架构视角下的 Kernel 开发决策
抽象硬件架构最终落实到 SIMD Kernel 的开发流程中,可归纳为以下决策链:
- 确定计算方式:根据数据访问和计算特点选择 Tile 矢量(UB 载体)、Reg 矢量(Register File 载体)或 Cube 矩阵(L0/L1 Buffer 载体)计算;
- 设计多核与 Tile 切分:确定
block_dim、各核的数据范围、Tile shape 和尾块处理方式; - 声明 Tensor 参数:在 Kernel 签名中描述 GM 输入、输出和 Workspace 的数据类型、shape 及 layout;
- 规划片上数据:根据计算方式选择 Tile 所在的
MemorySpace和数据排布(ND/NZ/ZN 等 layout、fractal、pad、compact); - 编写执行域:在
section_vector()或section_cube()中表达相应的 SIMD 计算; - 编译和启动:使用
@pypto_pro.language.jit编译 Kernel,在 Host 侧通过kernelstream, block_dim启动。
核心要点回顾:
- 指令流 ≠ 数据流:Scalar 发射任务,搬运/计算单元按各自数据路径异步执行,源码顺序不代表硬件完成顺序;
- 数据跨越流水必须同步:同步只约束执行顺序、不搬运数据,推荐使用 TileGroup +
auto_mutex=True,精确控制场景用sync_src/sync_dst; - 片上存储是分级的:AIV 侧 GM→UB→Register File,AIC 侧 GM→L1→L0A/L0B→L0C,选择计算方式前先确认数据路径;
- 多核映射有固定规则:纯 Vector/Cube 与混合 Kernel 的核索引语义不同,混合 Kernel 采用 AIC:AIV = 1:2 的执行组映射。
相关深入阅读:SIMD编程范式、Tile创建和操作、Tile计算、Reg计算、Cube计算、多核Tiling切分。
【免费下载链接】pyptoPyPTO(发音: pai p-t-o):Parallel Tensor/Tile Operation编程范式。项目地址: https://gitcode.com/cann/pypto
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考