
CANN pyasc 标量加法接口asc.language.basic.adds使用与原理深度解析【免费下载链接】pyasc本项目为Python用户提供算子编程接口支持在昇腾AI处理器上加速计算接口与Ascend C一一对应并遵守Python原生语法。项目地址: https://gitcode.com/cann/pyasc导读asc.language.basic.adds是 CANN pyasc 为 Python 开发者提供的矢量与标量求和接口它把 Ascend C 中的Adds矢量指令封装为符合 Python 原生语法的调用方式用于在昇腾 AI 处理器的矢量计算单元上高效完成每个元素 同一个标量的广播式运算如张量整体平移、偏置累加。读完本文你将掌握adds的全部三种重载形态count模式、mask 连续模式、mask 逐 bit 模式及其参数语义理解其底层的 IR 分发与 L0/L1/L2 三级指令生成机制并能参照仓库内的测试用例写出可直接运行的 JIT 内核代码。一、接口定位从 Ascend C 到 Python 的一一对应adds位于 python/asc/language/basic/vec_binary_scalar.py与leaky_relu、maxs、mins、muls、shift_left、shift_right等接口共同组成矢量与标量二元运算家族。该文件顶部通过三个overload装饰器声明了接口的三种调用形态实际实现则通过装饰器set_binary_scalar_docstring(cpp_nameAdds, append_text矢量内每个元素与标量求和。)自动注入与 Ascend C 原型对应的文档字符串docs 目录下的 asc.language.basic.adds.md 正是由此生成体现了接口与 Ascend C 一一对应、遵守 Python 原生语法的项目设计宗旨。对应的 Ascend C 函数原型该接口对应以下三种 Ascend C 原型见 vec_binary_scalar.py 的文档说明template typename T, bool isSetMask true __aicore__ inline void Adds(const LocalTensorT dstLocal, const LocalTensorT srcLocal, const T scalarValue, const int32_t calCount) template typename T, bool isSetMask true __aicore__ inline void Adds(const LocalTensorT dstLocal, const LocalTensorT srcLocal, const T scalarValue, uint64_t mask[], const uint8_t repeatTimes, const UnaryRepeatParams repeatParams) template typename T, bool isSetMask true __aicore__ inline void Adds(const LocalTensorT dstLocal, const LocalTensorT srcLocal, const T scalarValue, uint64_t mask, const uint8_t repeatTimes, const UnaryRepeatParams repeatParams)三种原型分别对应 Python 侧三种重载count模式计算前 n 个元素、mask 逐 bit 模式uint64_t mask[]数组、mask 连续模式单个uint64_t mask值。二、函数签名与参数语义完整函数签名asc.language.basic.adds共提供三个重载asc.language.basic.adds(dst: LocalTensor, src: LocalTensor, scalar: int | float, count: int, is_set_mask: bool True) → None asc.language.basic.adds(dst: LocalTensor, src: LocalTensor, scalar: int | float, mask: int, repeat_times: int, repeat_params: UnaryRepeatParams, is_set_mask: bool True) → None asc.language.basic.adds(dst: LocalTensor, src: LocalTensor, scalar: int | float, mask: List[int], repeat_times: int, repeat_params: UnaryRepeatParams, is_set_mask: bool True) → None参数说明参数类型说明dstLocalTensor目的操作数支持的 TPosition 为 VECIN / VECCALC / VECOUTsrcLocalTensor源操作数支持的 TPosition 为 VECIN / VECCALC / VECOUTscalarint | float标量源操作数数据类型需与目的操作数中的元素类型保持一致countint参与计算的元素个数count 模式专用maskint | List[int]控制每次迭代内参与计算的元素。int 为连续模式List[int] 为逐 bit 模式repeat_timesint重复迭代次数repeat_paramsUnaryRepeatParams元素操作控制结构信息控制迭代内的 block 间与迭代间的数据搬移步长is_set_maskbool是否在接口内部设置 mask 模式和 mask 值默认 True约束说明操作数地址对齐要求与地址重叠约束遵循 Ascend C 算子开发接口的通用说明和约束章节通用地址对齐约束、通用地址重叠约束adds作为矢量指令同样要求操作数起始地址满足 32 字节对齐等硬件约束。接口为就地运算风格dst[i] src[i] scalar调用后结果写入dst因此dst与src的 TPosition 均可为 VECIN/VECCALC/VECOUT便于在流水线中灵活选择计算缓冲位置。三、三种调用模式与示例1. count 模式前 n 个元素计算适用于不关心 mask 细节、只对张量头部连续count个元素做加法的场景也是日常最常用的简化形态asc.adds(dst, src, scalar, count512)该调用等价于对src的前 512 个元素执行dst[i] src[i] scalar。2. mask 连续模式高维切分 迭代当需要处理超过单次硬件处理能力的元素数量时通过maskrepeat_timesrepeat_params组合实现分块迭代mask 128 scalar 2 # repeat_times 4一次迭代计算128个数共计算512个数 # dst_blk_stride, src_blk_stride 1单次迭代内数据连续读取和写入 # dst_rep_stride, src_rep_stride 8相邻迭代间数据连续读取和写入 params asc.UnaryRepeatParams(1, 1, 8, 8) asc.adds(dst, src, scalar, maskmask, repeat_times4, repeat_paramsparams)mask 连续模式下mask 128表示每次迭代连续计算 128 个元素迭代 4 次共计算 512 个元素。3. mask 逐 bit 模式按位控制参与元素当需要精细控制每次迭代内哪些元素参与计算例如非连续、跳跃式布局时使用List[int]形式的 maskmask [uint64_max, uint64_max] scalar 2 # repeat_times 4一次迭代计算128个数共计算512个数 # dst_blk_stride, src_blk_stride 1单次迭代内数据连续读取和写入 # dst_rep_stride, src_rep_stride 8相邻迭代间数据连续读取和写入 params asc.UnaryRepeatParams(1, 1, 8, 8) asc.adds(dst, src, scalar, maskmask, repeat_times4, repeat_paramsparams)列表中的每个元素是 64 位无符号整数其每一位控制一个元素是否参与计算1 表示参与。示例中两个uint64_max全 1拼成 128 bit与连续模式的mask 128在效果上等价但逐 bit 模式支持任意位组合表达能力更强。UnaryRepeatParams 详解UnaryRepeatParams定义于 python/asc/language/core/types.py其构造参数及默认值如下UnaryRepeatParams(dst_blk_stride: RuntimeInt 1, src_blk_stride: RuntimeInt 1, dst_rep_stride: RuntimeInt 8, src_rep_stride: RuntimeInt 8)四个字段分别控制数据搬移步长底层通过asc_ConstructOp构建为 IR 值字段类型依次为ui16 / ui16 / ui8 / ui8dst_blk_stride / src_blk_stride单次迭代内相邻 block数据块之间的目的/源地址步长单位为 block32B。设为 1 表示块内数据连续。dst_rep_stride / src_rep_stride相邻迭代repeat之间的目的/源地址步长单位为 block。设为 8 表示相邻迭代数据紧密衔接如 int32 场景下 128 元素 × 4 字节 / 32 字节 16 block示例取 8 适用于半精度等场景需按数据类型换算。四、源码级实现三级 IR 指令分发adds的真实实现位于 vec_binary_scalar.py主体只有几行require_jit set_binary_scalar_docstring(cpp_nameAdds, append_text矢量内每个元素与标量求和。) def adds(dst: LocalTensor, src: LocalTensor, scalar: RuntimeNumeric, *args, **kwargs) - None: builder global_builder.get_ir_builder() op_impl(adds, dst, src, scalar, args, kwargs, builder.create_asc_AddsL0Op, builder.create_asc_AddsL1Op, builder.create_asc_AddsL2Op)require_jit保证该函数只在 JIT 编译上下文中执行核心逻辑全部收敛到 utils.py 的vec_binary_scalar_op_impl中通过OverloadDispatcher依据实参形态自动匹配到三种模式参数形态分发目标底层 IR Op说明mask: intrepeat_timesrepeat_paramsbuild_l0asc_AddsL0Opmask 连续模式mask 转 int64repeat_times 转 int8mask: listrepeat_timesrepeat_paramsbuild_l1asc_AddsL1Opmask 逐 bit 模式列表元素逐个转 uint64countbuild_l2asc_AddsL2Opcount 模式count 转 int32关键实现细节utils.py类型收紧scalar会先经_mat(scalar, src.dtype)按源张量元素类型做常量材料化再转 IR从源头保证标量与元素类型一致mask 连续模式mask被转为int64的 IR 常量repeat_times转为int8与硬件迭代计数寄存器位宽对应mask 逐 bit 模式列表中的每个元素先转为uint64IR 值再以数组形式传入build_l1count 模式count转为int32与 Ascend C 原型中const int32_t calCount严格对应。在 C 侧这些 IR Op 定义于 include/ascir/Target/Asc/Basic/VecBinaryScalar.hAddsL0Op等与SubsL0Op、LeakyReluL0Op、MaxsL0Op等同属VecScalarL0Op家族最终由 lib/Target/AscendC/Translation.cpp 翻译为 Ascend C 的Adds指令代码完成从 Python 调用到昇腾矢量指令的完整链路。五、端到端实战一个可运行的 adds JIT 内核仓库测试 python/test/generalization/basic/test_vadds.py 给出了adds在完整算子流水线搬入 → 计算 → 搬出中的标准用法可直接作为编写自定义算子的模板import asc import asc.runtime.config as config import asc.lib.runtime as rt asc.jit def vadds_kernel(x: asc.GlobalAddress, scalar, z: asc.GlobalAddress, block_length: asc.ConstExpr[int], buffer_num: asc.ConstExpr[int], tile_length: asc.ConstExpr[int], tile_num: asc.ConstExpr[int]): offset asc.get_block_idx() * block_length x_gm asc.GlobalTensor() z_gm asc.GlobalTensor() x_gm.set_global_buffer(x offset) z_gm.set_global_buffer(z offset) pipe asc.TPipe() in_queue_x asc.TQue(asc.TPosition.VECIN, buffer_num) out_queue_z asc.TQue(asc.TPosition.VECOUT, buffer_num) pipe.init_buffer(quein_queue_x, numbuffer_num, lentile_length * x.dtype.sizeof()) pipe.init_buffer(queout_queue_z, numbuffer_num, lentile_length * z.dtype.sizeof()) for i in range(tile_num): copy_in(i, x_gm, in_queue_x, tile_length) compute(scalar, z_gm, in_queue_x, out_queue_z, tile_length) copy_out(i, z_gm, out_queue_z, tile_length) asc.jit def compute(scalar, z_gm: asc.GlobalTensor, in_queue_x: asc.TQue, out_queue_z: asc.TQue, tile_length: asc.ConstExpr[int]): x_local in_queue_x.deque(z_gm.dtype) z_local out_queue_z.alloc_tensor(z_gm.dtype) asc.adds(z_local, x_local, scalar, counttile_length) out_queue_z.enque(z_local) in_queue_x.free_tensor(x_local)该示例展示了adds在实际内核中的关键配合TPosition 选择源张量从VECIN队列deque取出目的张量在VECOUT队列alloc_tensor与接口支持的 TPosition 范围一致count 模式调用asc.adds(z_local, x_local, scalar, counttile_length)在每个 tile 内完成一次标量加法tile 大小通过tile_length控制多核并行asc.get_block_idx() * block_length计算当前核负责的数据偏移实现多核分块正确性验证测试中assert torch.allclose(z, x scalar)直接与 PyTorch 的广播加法结果对比验证了adds语义与x scalar完全等价。测试覆盖torch.float32 / float16 / int32 / int16等多种数据类型及(1000,)、(1,)、(9999,)、(2048,)、(8192,)、(153, 834)等不同形状见 test_vadds.py整数类型使用随机整数、浮点类型使用正态分布随机数从多维度验证了adds在 NPU 后端config.Backend.NPU上的正确性。六、与同类标量运算接口的对比adds并非孤立接口在 vec_binary_scalar.py 中与之共享同一套vec_binary_scalar_op_impl分发的还有接口语义说明addsdst src scalar矢量元素与标量求和maxsdst max(src, scalar)逐元素取与标量的较大值minsdst min(src, scalar)逐元素取与标量的较小值mulsdst src × scalar矢量元素与标量求积leaky_reluLeaky ReLU 激活分段线性激活函数shift_left/shift_right逐元素按位左移/右移移位位数由 scalar 决定这些接口共享完全相同的三重重载形态和参数语义count/mask连续 /mask逐 bit掌握adds的用法即可触类旁通地使用其余标量运算接口这也是该系列接口设计高度一致性的体现。总结asc.language.basic.adds是 CANN pyasc 中矢量标量运算家族的基石接口通过三重重载覆盖了前 n 个元素计算mask 连续迭代mask 逐 bit 精细控制三类典型场景。其实现将 Python 层参数分发vec_binary_scalar_op_impl与 Ascend C 三级指令生成L0/L1/L2无缝衔接既保持了 Python 原生的简洁调用体验又完整保留了硬件指令的表达能力。结合UnaryRepeatParams的步长控制与TPipe/TQue流水线机制开发者可以在昇腾 AI 处理器上高效实现各类需要标量广播运算的算子内核。【免费下载链接】pyasc本项目为Python用户提供算子编程接口支持在昇腾AI处理器上加速计算接口与Ascend C一一对应并遵守Python原生语法。项目地址: https://gitcode.com/cann/pyasc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考