在 pyasc 中使用 asc.language.adv.power 实现按元素幂运算
2026/9/18 1:34:48 网站建设 项目流程

在 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用于按元素做幂运算,即对两个源操作数src0src1逐元素计算:

dst[i] = src0[i] ** src1[i]

其中src0是底数、src1是指数,结果写回目的操作数dst。它是 pyasc 高阶 API(asc.language.adv命名空间)中少有的二元数学运算接口(区别于sincosexp等一元接口),与按位异或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
参数类型说明
dstLocalTensor目的操作数,支持的 TPosition 为 VECIN / VECCALC / VECOUT。
src0LocalTensor源操作数(底数),支持的 TPosition 为 VECIN / VECCALC / VECOUT,数据类型需与dst保持一致。
src1LocalTensor源操作数(指数),支持的 TPosition 为 VECIN / VECCALC / VECOUT,数据类型需与dst保持一致。
countint | None参与计算的元素个数,默认None表示对整块缓冲区执行计算。
temp_bufferLocalTensor | None临时内存空间,类型为 LocalTensor,支持的 TPosition 为 VECIN / VECCALC / VECOUT,通常以uint8类型的 VECCALC 张量提供。
is_reuse_sourcebool是否允许修改源操作数,默认值为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)

注意几个关键对应关系:

  • dstdstTensorsrc0src0Tensorsrc1src1Tensor
  • countcalCount(uint32_t,参与计算的元素个数);
  • temp_buffersharedTmpBufferLocalTensor<uint8_t>,即共享临时缓冲区,这正是测试中临时张量使用uint8类型的原因);
  • is_reuse_source→ 模板参数isReuseSource(默认false)。

底层实现原理:从 Python 调用到 IR 构建

power的实现非常精简,真正的逻辑都收敛在math_op_impl中。该函数是所有数学类高阶 API(sincosexppowerxor等)共用的底层实现,源码见 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)

其核心处理流程可以概括为四条:

  1. 类型校验count必须是RuntimeInt(对应int32类型),temp_buffer必须是LocalTensor
  2. 常量物化:通过materialize_ir_value_mat)将count物化为int32、将is_reuse_source物化为bit类型的 IR 常量;
  3. 张量转换:把所有LocalTensor参数通过to_ir()转换为 IR 值;
  4. 算子构建:动态调用 IR builder 上的create_asc_PowerOp,传入sharedTmpBuffercalCountisReuseSource三个关键字参数,完成算子 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不能与src0src1指向同一段缓冲区;
  • 地址对齐:操作数地址对齐要求请参见《Ascend C 算子开发接口》中的"通用说明和约束-通用地址对齐约束"(文档对应链接见 asc.language.adv.power 官方文档);
  • 数据类型一致src0src1的数据类型需要与dst保持一致;
  • TPosition 限制:所有张量均须位于 VECIN / VECCALC / VECOUT 位置(向量计算相关位置)。

调用示例

官方文档给出的最小调用形式为:

asc.adv.power(dst, src0, src1)

即省略counttemp_bufferis_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 的实操要点:

  1. 张量声明:使用asc.LocalTensor(dtype=..., pos=asc.TPosition.X, addr=..., tile_size=...)声明片上张量,dtype统一为asc.float16,源/目的位置分别用VECINVECOUT
  2. 临时缓冲区temp_buffer声明为dtype=asc.uint8pos=asc.TPosition.VECCALC的 LocalTensor,与 Ascend C 原型中的LocalTensor<uint8_t> sharedTmpBuffer对应;
  3. 两种调用形态:带temp_buffer的调用(内部自动匹配带sharedTmpBuffer的 C++ 重载)与不带temp_buffer的调用(匹配不带临时缓冲区的重载)可以同时出现在同一 kernel 中;
  4. count语义count=512表示本次计算参与幂运算的元素个数为 512,对应 C++ 侧的calCount=512
  5. JIT 编译:kernel 函数需用@asc.jit装饰,并通过power_kernel[1]()以指定核数(1 核)启动,随后由 pyasc 运行时完成编译与mock_launcher_run启动。

常见问题与使用建议

  • powerxor的区别:二者都是双输入逐元素接口,但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(sincosexpsqrtxor等)均可参考同一实现模式,见 python/asc/language/adv/math.py 与 asc.language.adv 文档总览。

【免费下载链接】pyasc本项目为Python用户提供算子编程接口,支持在昇腾AI处理器上加速计算,接口与Ascend C一一对应并遵守Python原生语法。项目地址: https://gitcode.com/cann/pyasc

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询