pyasc TPipe.init_buffer 详解:为 TQue 队列与 TBuf 临时变量分配 Device 端内存

发布时间:2026/9/19 7:59:38
pyasc TPipe.init_buffer 详解:为 TQue 队列与 TBuf 临时变量分配 Device 端内存 pyasc TPipe.init_buffer 详解为 TQue 队列与 TBuf 临时变量分配 Device 端内存【免费下载链接】pyasc本项目为Python用户提供算子编程接口支持在昇腾AI处理器上加速计算接口与Ascend C一一对应并遵守Python原生语法。项目地址: https://gitcode.com/cann/pyascasc.language.fwk.TPipe.init_buffer是 CANN pyasc 算子编程接口中负责 Device 端如 Unified Buffer、L1 Buffer 等内存分配的核心 API它为一类 Kernel 中必须且只能存在一个的TPipe资源管理器提供了两种内存分配入口为流水队列TQue分配多块内存支持 double buffer 双缓冲以及为临时变量容器TBuf分配单块内存。读完本文你将掌握init_buffer两种重载的完整语义、参数取值与 32 字节对齐规则、double buffer 的开启方式、内存生命周期约束并能结合仓库示例examples/02_add_framework/add_framework.py与源码python/asc/language/fwk/tpipe.py写出可直接运行的流水化算子。一、init_buffer 在 pyasc 内存管理中的定位在 pyasc 中一个 Kernel 函数必须且只能初始化一个TPipe对象对应源码中TPipeManager的全局唯一实例管理见 python/asc/language/fwk/tpipe.py 的TPipeManager.set/get实现。TPipe统一管理 Device 端内存与同步事件其主要能力包括内存资源管理通过init_buffer接口为TQue队列和TBuf临时变量缓冲区分配内存同步事件管理通过alloc_event_id、release_event_id等接口申请和释放事件 ID用于流水线同步控制。其中init_buffer是内存管理的人口后续所有基于TQue的alloc_tensor/deque/enque以及基于TBuf的get/get_with_offset都建立在其分配出的内存块之上。对应地在 docs/python-api/language/fwk.md 的TPipe章节中init_buffer被明确描述为用于为 TQue 等队列和 TBuf 分配内存。二、函数签名与两种重载形式TPipe.init_buffer提供两个重载分别面向队列对象与临时缓冲区对象# 重载 1为 TQue 等队列分配内存 TPipe.init_buffer(que: TQue, num: int 0, len: int 0) - None # 重载 2为 TBuf 分配内存 TPipe.init_buffer(buf: TBuf, len: int 0) - None其对应的 Ascend C 函数原型分别为template class T __aicore__ inline bool InitBuffer(T que, uint8_t num, uint32_t len) template TPosition bufPos __aicore__ inline bool InitBuffer(TBufbufPos buf, uint32_t len)在 pyasc 的 Python 侧这两种重载通过OverloadDispatcher分发实现python/asc/language/fwk/tpipe.pydispatcher.register(queTQue, numRuntimeInt, lenRuntimeInt) def _(que: TQue, num: RuntimeInt 0, len: RuntimeInt 0): global_builder.get_ir_builder().create_asc_TPipeInitQueueOp(self.to_ir(), que.to_ir(), _mat(num, KnownTypes.int_).to_ir(), _mat(len, KnownTypes.int_).to_ir()) dispatcher.register(bufTBuf, lenRuntimeInt) def _(buf: TBuf, len: RuntimeInt 0): global_builder.get_ir_builder().create_asc_TPipeInitBufferOp(self.to_ir(), buf.to_ir(), _mat(len, KnownTypes.int_).to_ir())可以看到init_buffer最终会分别生成asc_TPipeInitQueueOp队列内存初始化与asc_TPipeInitBufferOp缓冲区内存初始化两种 IR 算子将TPipe、TQue/TBuf及内存参数一起下发给编译后端进而映射为昇腾指令侧的缓冲区初始化逻辑。三、参数说明与取值细节3.1 que num len队列重载参数类型说明queTQue需要分配内存的 TQue 等队列对象通常以asc.TQue(asc.TPosition.VECIN, depth)形式创建numint分配内存块的个数。double buffer 功能通过该参数开启num设置为 1 表示不开启 double buffer设置为 2 表示开启 double bufferlenint每个内存块的大小单位为字节。当传入的len不满足 32 字节对齐时API 内部会自动向上补齐至 32 字节对齐后续的数据搬运过程会涉及非对齐处理que的类型可以是TQue也可以是TQueBindTQue继承自TQueBindTQue是TQueBind的简化模式。TQue构造时传入的TPosition逻辑位置决定了内存分配的位置如VECIN、VECOUT、VECCALC等TPosition枚举定义见 python/asc/language/core/enums.py常用取值包括GM全局内存、A1/A2/B1/B2/C1/C2Cube 侧逻辑位置、CO1/CO2VECIN、VECOUT、VECCALC向量计算侧逻辑位置。3.2 buf len缓冲区重载参数类型说明bufTBuf需要分配内存的 TBuf 对象用于管理临时变量占用的内存存储位置通过TBuf(pos)构造时的TPosition指定lenint为 TBuf 分配的内存大小单位为字节。与队列重载相同非 32 字节对齐的len会被 API 内部自动向上补齐至 32 字节对齐在 pyasc 中TBuf同样继承自TQueBind其占用的存储空间由TPipe管理。init_buffer完成内存初始化后即可通过TBuf.get(dtype, len)或TBuf.get_with_offset(size, buf_offset, dtype)获取指定长度的LocalTensor参与计算。值得注意的是TBuf.get_with_offset在源码中对偏移量做了显式的 32 字节对齐校验python/asc/language/fwk/tpipe.py 中if buf_offset % 32 ! 0: raise ValueError(buf_offset must be align to 32B.)与init_buffer内部的 32 字节对齐规则保持一致。四、约束说明使用init_buffer时需要遵守以下约束自动释放init_buffer申请的内存会在TPipe对象销毁时通过析构函数自动释放无需手动释放。重新分配需先 reset如果需要重新分配init_buffer申请的内存应先调用TPipe.reset()再调用init_buffer。reset完成资源的释放与 eventId 等变量的初始化操作使TPipe恢复到初始化状态详见 docs/python-api/language/generated/asc.language.fwk.TPipe.reset.md。Buffer 总数上限一个 Kernel 中所有使用的 Buffer 数量之和不能超过 64。五、调用示例为 TQue 与 TBuf 分配内存以下示例完整继承自 docs/python-api/language/generated/asc.language.fwk.TPipe.init_buffer.md展示了两种重载的标准用法# 为TQue分配内存分配内存块数为2每块大小为128字节num2开启double buffer pipe asc.Tpipe() que asc.TQue(asc.TPosition.VECOUT, 2) num 2 len 128 pipe.init_buffer(queque, numnum, lenlen) # 为TBuf分配内存分配长度为128字节 pipe asc.Tpipe() buf asc.TBuf(asc.TPosition.A1) len 128 pipe.init_buffer(bufbuf, lenlen)六、实战结合 double buffer 编写流水化算子num参数对 double buffer 的控制是init_buffer最重要的实战价值。以仓库示例 examples/02_add_framework/add_framework.py 为例一个采用 2 级缓冲BUFFER_NUM 2的向量加法 Kernel 完整展示了init_buffer的典型用法BUFFER_NUM 2 # BUFFER_NUM should be 1 or 2 asc.jit def vadd_kernel(x: asc.GlobalAddress, y: asc.GlobalAddress, z: asc.GlobalAddress, block_length: int, tile_length: asc.ConstExpr[int]): offset asc.get_block_idx() * block_length x_gm asc.GlobalTensor() y_gm asc.GlobalTensor() z_gm asc.GlobalTensor() x_gm.set_global_buffer(x offset) y_gm.set_global_buffer(y offset) z_gm.set_global_buffer(z offset) pipe asc.TPipe() in_queue_x asc.TQue(asc.TPosition.VECIN, BUFFER_NUM) in_queue_y asc.TQue(asc.TPosition.VECIN, BUFFER_NUM) out_queue_z asc.TQue(asc.TPosition.VECOUT, BUFFER_NUM) pipe.init_buffer(in_queue_x, BUFFER_NUM, tile_length * x.dtype.sizeof()) pipe.init_buffer(in_queue_y, BUFFER_NUM, tile_length * y.dtype.sizeof()) pipe.init_buffer(out_queue_z, BUFFER_NUM, tile_length * z.dtype.sizeof()) for i in range(TILE_NUM * BUFFER_NUM): copy_in(i, x_gm, y_gm, in_queue_x, in_queue_y, tile_length) compute(z_gm, in_queue_x, in_queue_y, out_queue_z, tile_length) copy_out(i, z_gm, out_queue_z, tile_length)该示例中的三个要点队列深度与内存块数对应TQue(asc.TPosition.VECIN, BUFFER_NUM)的 depth 与init_buffer的num保持一致均为BUFFER_NUM即每个队列可容纳两块缓冲实现搬入copy_in与计算compute、搬出copy_out的流水重叠。len 按数据类型换算len tile_length * dtype.sizeof()其中dtype.sizeof()返回单个元素的字节数保证每块内存恰好容纳一个 tile 的数据。alloc_tensor与init_buffer的联动TQue.alloc_tensor分配的 Tensor 大小即为init_buffer时设置的每块内存长度docs/python-api/language/fwk.md 中TQue.alloc_tensor的描述因此len的设置直接决定后续data_copy、add等算子可操作的数据量。类似的双缓冲写法在仓库测试中大量出现例如 python/test/generalization/basic/test_vadd.py 同样以buffer_num同时作为队列 depth 与init_buffer的num并在copy_in/compute/copy_out三段流水间通过队列完成同步。七、实战TBuf 临时缓冲区的分配与使用TBuf用于管理 Kernel 内临时变量占用的内存适用于向量计算中需要中间缓冲区的场景。结合 docs/python-api/language/generated/asc.language.fwk.TBuf.get.md 中的示例与仓库测试 python/test/generalization/adv/test_quant.py 的用法完整链路如下# 为TBuf初始化分配内存分配内存长度为1024字节 pipe asc.Tpipe() calc_buf asc.TBuf(asc.TPosition.VECCALC) byte_len 1024 pipe.init_buffer(calc_buf, byte_len) # 从calc_buf获取TensorTensor为pipe分配的所有内存大小为1024字节 temp_tensor1 calc_buf.get(asc.int32) # 从calc_buf获取TensorTensor为128个int32_t类型元素的内存大小为512字节 temp_tensor1 calc_buf.get(asc.int32, 128)仓库测试 python/test/generalization/adv/test_quant.py 中的实际使用模式与之完全一致tmp_buf asc.TBuf(asc.TPosition.VECCALC) pipe.init_buffer(buftmp_buf, lentmp_min_bytes) tmp_local tmp_buf.get(asc.uint8)使用TBuf.get时需要注意len的数值是 Tensor 中元素的个数len * sizeof(T)不能超过init_buffer时设置的 TBuf 初始化长度若要按字节偏移取子块则使用get_with_offset且偏移量必须 32 字节对齐。八、与 TPipe 生命周期管理的配合init_buffer分配的内存生命周期由TPipe统一管理在实际 Kernel 中通常与以下接口配合使用TPipe.reset()释放资源并初始化 eventId使TPipe恢复到初始化状态之后可再次调用init_buffer重新分配。典型的多轮复用模式来自 docs/python-api/language/generated/asc.language.fwk.TPipe.reset.mdpipe asc.Tpipe() que asc.TQue(asc.TPosition.VECOUT, 1) num 1 len 192 * 1024 for i in range(2): pipe.init_buffer(queque, numnum, lenlen) ... # process pipe.reset()TPipe.init_buf_pool()/TBufPool.init_buffer()在内存资源有限、需要手动指定 UB/L1 内存资源复用的场景下先用TPipe.init_buf_pool划分整块资源池再用TBufPool.init_buffer为其中的TQue/TBuf分配内存详见 docs/python-api/language/fwk.md 的TBufPool章节。此时TBufPool声明时通过buf_id_size指定可分配 Buffer 的最大数量默认上限为 4最大为 16。TPipe.destroy()显式释放资源而普通场景下不调用destroy时init_buffer申请的内存也会在TPipe对象销毁时自动释放。九、小结TPipe.init_buffer是 pyasc 算子编写中内存初始化的起点两种重载分别面向流水队列TQueque num len与临时缓冲TBufbuf lennum参数同时承担内存块个数与double buffer 开关两个角色1 关闭、2 开启len参数以字节为单位非 32 字节对齐时会自动向上补齐与TBuf.get_with_offset的 32 字节对齐校验、data_copy等算子的对齐要求保持一致生命周期约束包括 TPipe 析构自动释放、重新分配前需reset、单 Kernel Buffer 总数不超过 64源码层面该接口通过OverloadDispatcher分发为asc_TPipeInitQueueOp与asc_TPipeInitBufferOp两种 IR 算子python/asc/language/fwk/tpipe.py是理解 pyasc 内存分配链路的重要入口。掌握init_buffer后可进一步阅读 docs/python-api/language/fwk.md 中的TQuealloc_tensor/deque/enque、TBufget/get_with_offset、TQueBind与TBufPool系列接口并结合 examples/02_add_framework/add_framework.py、examples/01_add/add.py 等示例构建完整的流水化算子。【免费下载链接】pyasc本项目为Python用户提供算子编程接口支持在昇腾AI处理器上加速计算接口与Ascend C一一对应并遵守Python原生语法。项目地址: https://gitcode.com/cann/pyasc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考