在 pyasc 中使用 asc.language.adv.power 实现按元素幂运算
【免费下载链接】pyasc本项目为Python用户提供算子编程接口,支持在昇腾AI处理器上加速计算,接口与Ascend C一一对应并遵守Python原生语法。项目地址: https://gitcode.com/cann/pyasc
本文围绕 CANN pyasc 项目(为 Python 用户提供与 Ascend C 一一对应的算子编程接口)中的asc.language.adv.power高阶 API,系统讲解其函数签名、参数语义、对应的 Ascend C 函数原型、底层 IR 构建链路与约束条件,并结合仓库源码与单元测试给出可直接运行的双输入幂运算 kernel 写法。读完本文,你将能够在昇腾 AI 处理器上正确、高效地编写按元素幂(Power)运算算子。
功能概述
asc.language.adv.power用于按元素做幂运算,即对两个源操作数src0与src1逐元素计算:
dst[i] = src0[i] ** src1[i]其中src0是底数、src1是指数,结果写回目的操作数dst。它是 pyasc 高阶 API(asc.language.adv命名空间)中少有的二元数学运算接口(区别于sin、cos、exp等一元接口),与按位异或xor在形式上相似但语义完全不同。
该接口在仓库中的官方文档位于 docs/python-api/language/generated/asc.language.adv.power.md,实现位于 python/asc/language/adv/math.py,与 Ascend C 的Power算子原语一一对应。
函数签名与参数说明
asc.language.adv.power(dst: LocalTensor, src0: LocalTensor, src1: LocalTensor, count: int | None = None, temp_buffer: LocalTensor | None = None, is_reuse_source: bool = False) -> None| 参数 | 类型 | 说明 |
|---|---|---|
dst | LocalTensor | 目的操作数,支持的 TPosition 为 VECIN / VECCALC / VECOUT。 |
src0 | LocalTensor | 源操作数(底数),支持的 TPosition 为 VECIN / VECCALC / VECOUT,数据类型需与dst保持一致。 |
src1 | LocalTensor | 源操作数(指数),支持的 TPosition 为 VECIN / VECCALC / VECOUT,数据类型需与dst保持一致。 |
count | int | None | 参与计算的元素个数,默认None表示对整块缓冲区执行计算。 |
temp_buffer | LocalTensor | None | 临时内存空间,类型为 LocalTensor,支持的 TPosition 为 VECIN / VECCALC / VECOUT,通常以uint8类型的 VECCALC 张量提供。 |
is_reuse_source | bool | 是否允许修改源操作数,默认值为false。 |
其中LocalTensor的完整定义可参考 asc.language.core.LocalTensor,相关文档索引见 asc.language.adv 总览。
从源码看,power同时提供了静态类型版本(count: Optional[int]、is_reuse_source: bool)与 JIT 运行时版本(count: Optional[RuntimeInt]、is_reuse_source: RuntimeBool)两个重载,前者用于 IDE 类型提示与静态检查,后者在@asc.jit编译场景下实际生效,源码见 math.py#L392-L446:
@overload def power(dst: LocalTensor, src0: LocalTensor, src1: LocalTensor, count: Optional[int] = None, temp_buffer: Optional[LocalTensor] = None, is_reuse_source: bool = False) -> None: ... @require_jit def power(dst: LocalTensor, src0: LocalTensor, src1: LocalTensor, count: Optional[RuntimeInt] = None, temp_buffer: Optional[LocalTensor] = None, is_reuse_source: RuntimeBool = False) -> None: ... math_op_impl((dst, src0, src1), count, temp_buffer, is_reuse_source, "create_asc_PowerOp")对应的 Ascend C 函数原型
asc.language.adv.power与 Ascend C 的Power模板函数一一对应。pyasc 会根据是否传入count(即calCount)和temp_buffer(即sharedTmpBuffer)自动选择底层函数重载,对应的四个 C++ 原型如下:
template <typename T, bool isReuseSource = false> __aicore__ inline void Power(const LocalTensor<T>& dstTensor, const LocalTensor<T>& src0Tensor, const LocalTensor<T>& src1Tensor, const LocalTensor<uint8_t>& sharedTmpBuffer, uint32_t calCount) template <typename T, bool isReuseSource = false> __aicore__ inline void Power(const LocalTensor<T>& dstTensor, const LocalTensor<T>& src0Tensor, const LocalTensor<T>& src1Tensor, const LocalTensor<uint8_t>& sharedTmpBuffer) template <typename T, bool isReuseSource = false> __aicore__ inline void Power(const LocalTensor<T>& dstTensor, const LocalTensor<T>& src0Tensor, const LocalTensor<T>& src1Tensor, uint32_t calCount) template <typename T, bool isReuseSource = false> __aicore__ inline void Power(const LocalTensor<T>& dstTensor, const LocalTensor<T>& src0Tensor, const LocalTensor<T>& src1Tensor)注意几个关键对应关系:
dst→dstTensor,src0→src0Tensor,src1→src1Tensor;count→calCount(uint32_t,参与计算的元素个数);temp_buffer→sharedTmpBuffer(LocalTensor<uint8_t>,即共享临时缓冲区,这正是测试中临时张量使用uint8类型的原因);is_reuse_source→ 模板参数isReuseSource(默认false)。
底层实现原理:从 Python 调用到 IR 构建
power的实现非常精简,真正的逻辑都收敛在math_op_impl中。该函数是所有数学类高阶 API(sin、cos、exp、power、xor等)共用的底层实现,源码见 math.py#L19-L29:
def math_op_impl(tensors: Tuple[LocalTensor], count: Optional[RuntimeInt], temp_buffer: Optional[LocalTensor], is_reuse_source: RuntimeBool, build_method: str) -> None: if count is not None: check_type("count", count, RuntimeInt) count = _mat(count, KnownTypes.int32).to_ir() if temp_buffer is not None: check_type("temp_buffer", temp_buffer, LocalTensor) temp_buffer = temp_buffer.to_ir() is_reuse_source = _mat(is_reuse_source, KnownTypes.bit).to_ir() getattr(global_builder.get_ir_builder(), build_method)(*(t.to_ir() for t in tensors), sharedTmpBuffer=temp_buffer, calCount=count, isReuseSource=is_reuse_source)其核心处理流程可以概括为四条:
- 类型校验:
count必须是RuntimeInt(对应int32类型),temp_buffer必须是LocalTensor; - 常量物化:通过
materialize_ir_value(_mat)将count物化为int32、将is_reuse_source物化为bit类型的 IR 常量; - 张量转换:把所有
LocalTensor参数通过to_ir()转换为 IR 值; - 算子构建:动态调用 IR builder 上的
create_asc_PowerOp,传入sharedTmpBuffer、calCount、isReuseSource三个关键字参数,完成算子 IR 节点的创建。
也就是说,一次asc.adv.power(dst, src0, src1, count=512, temp_buffer=tmp)调用,最终会在 IR 中构建出一个create_asc_PowerOp节点,之后再经过代码生成流水线翻译为对应的 Ascend C 代码。在发射端,PowerOp已在 lib/Target/AscendC/Translation.cpp#L128 中注册进翻译映射表(ascendc::PowerOp),确保 IR 能正确下译为 Ascend C 的Power原语并最终落盘为昇腾可执行的算子代码。
约束说明
使用asc.language.adv.power时需遵守以下约束:
- 地址不允许重叠:不支持源操作数与目的操作数地址重叠,即
dst不能与src0、src1指向同一段缓冲区; - 地址对齐:操作数地址对齐要求请参见《Ascend C 算子开发接口》中的"通用说明和约束-通用地址对齐约束"(文档对应链接见 asc.language.adv.power 官方文档);
- 数据类型一致:
src0、src1的数据类型需要与dst保持一致; - TPosition 限制:所有张量均须位于 VECIN / VECCALC / VECOUT 位置(向量计算相关位置)。
调用示例
官方文档给出的最小调用形式为:
asc.adv.power(dst, src0, src1)即省略count、temp_buffer、is_reuse_source三个可选参数,此时按整块缓冲区进行全量幂运算。
仓库单元测试中的完整用法
在仓库的单元测试 python/test/unit/language/adv/test_ops.py#L352-L364 中,test_power_kernel给出了一个完整、可运行参考的 kernel 写法,展示了temp_buffer传与不传两种形态:
def test_power_kernel(mock_launcher_run): @asc.jit def power_kernel(): x_local = asc.LocalTensor(dtype=asc.float16, pos=asc.TPosition.VECIN, addr=0, tile_size=512) y_local = asc.LocalTensor(dtype=asc.float16, pos=asc.TPosition.VECIN, addr=0, tile_size=512) z_local = asc.LocalTensor(dtype=asc.float16, pos=asc.TPosition.VECOUT, addr=0, tile_size=512) tmp = asc.LocalTensor(dtype=asc.uint8, pos=asc.TPosition.VECCALC, addr=0, tile_size=512) asc.adv.power(z_local, x_local, y_local, count=512, temp_buffer=tmp) asc.adv.power(z_local, x_local, y_local, count=512) power_kernel[1]() assert mock_launcher_run.call_count == 1从该测试可以提炼出编写powerkernel 的实操要点:
- 张量声明:使用
asc.LocalTensor(dtype=..., pos=asc.TPosition.X, addr=..., tile_size=...)声明片上张量,dtype统一为asc.float16,源/目的位置分别用VECIN与VECOUT; - 临时缓冲区:
temp_buffer声明为dtype=asc.uint8、pos=asc.TPosition.VECCALC的 LocalTensor,与 Ascend C 原型中的LocalTensor<uint8_t> sharedTmpBuffer对应; - 两种调用形态:带
temp_buffer的调用(内部自动匹配带sharedTmpBuffer的 C++ 重载)与不带temp_buffer的调用(匹配不带临时缓冲区的重载)可以同时出现在同一 kernel 中; count语义:count=512表示本次计算参与幂运算的元素个数为 512,对应 C++ 侧的calCount=512;- JIT 编译:kernel 函数需用
@asc.jit装饰,并通过power_kernel[1]()以指定核数(1 核)启动,随后由 pyasc 运行时完成编译与mock_launcher_run启动。
常见问题与使用建议
power与xor的区别:二者都是双输入逐元素接口,但power是数学幂运算(dst[i] = src0[i] ** src1[i]),xor是按位异或运算(dst[i] = src0[i] ^ src1[i]),且xor支持整型(如int16)输入,务必按业务语义选择;is_reuse_source的取舍:默认false表示不允许修改源操作数,更安全;若确知源数据后续不再使用、且希望释放临时空间或减少缓冲占用,可显式置True,但需自行承担源数据被覆盖的风险;- 地址重叠约束:由于不支持源与目的地址重叠,实践中应保证
dst使用独立缓冲区,或将同一数据先拷贝到新的VECCALC缓冲后再作为源输入; - 性能考量:幂运算涉及指数计算,需要临时中间缓冲,建议复用
temp_buffer并在循环外统一申请,避免反复分配;count精确指定参与元素数可避免对尾部无效数据的多余计算。
小结
asc.language.adv.power是 pyasc 高阶 API 中用于按元素幂运算的标准接口:它通过math_op_impl统一完成类型校验、常量物化与create_asc_PowerOp算子节点构建,并在翻译阶段映射到 Ascend C 的Power原语。结合本文给出的参数语义、四个 C++ 原型对应关系、约束条件以及源自仓库单元测试的完整 kernel 写法,你可以直接在昇腾 AI 处理器上编写正确的幂运算算子。更多数学类高阶 API(sin、cos、exp、sqrt、xor等)均可参考同一实现模式,见 python/asc/language/adv/math.py 与 asc.language.adv 文档总览。
【免费下载链接】pyasc本项目为Python用户提供算子编程接口,支持在昇腾AI处理器上加速计算,接口与Ascend C一一对应并遵守Python原生语法。项目地址: https://gitcode.com/cann/pyasc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考