- 人工智能
- 编译器
- 模型编译
- 高性能计算
- 深度学习
- CANN
【免费下载链接】pypto
PyPTO(发音: pai p-t-o):Parallel Tensor/Tile Operation编程范式。
导读
本文面向使用 CANN PyPTO(Parallel Tensor/Tile Operation 编程范式)在 Ascend 950 系列硬件上进行向量计算的开发者,系统讲解pypto_pro.language.cast这一 Tile 级逐元素类型转换接口的完整用法。文章覆盖接口函数原型、out/src/mode三参数的语义与约束、RoundMode 七种舍入模式的语义差异、30+ 条数据类型转换组合的完整支持矩阵、FP4 打包类型的特殊处理,以及 FP16→FP32 与 FP32→FP16 等实战代码示例,并辅以 Python 前端声明 与 NPU 代码生成映射 的源码级佐证。读完本文,你将能正确地在 PyPTO 内核中完成任意受支持的数据类型转换,并为精度敏感场景选择正确的舍入模式。
一、接口定位:Tile 级逐元素类型转换
pypto_pro.language.cast是 PyPTO Pro 语言中面向UB(Unified Buffer)Tile的数据类型转换接口,其行为可用以下公式概括:
$$ out_{i,j}=\operatorname{cast}{mode}(src{i,j}) $$
即:对源 Tile 有效区域内的每个元素逐一进行数据类型转换,并将结果写入目标 Tile 的对应逻辑坐标位置。目标数据类型完全由outTile 的dtype决定,接口本身不接收任何类型参数。
从源码看,该接口在 Python 前端中的声明位于 python/pypto_pro/language/_api.py:
@_api_decl def cast(out: Tile, src: Tile, *, mode: RoundMode = RoundMode.CAST_ROUND) -> None: """Cast Tile to a different data type. The target dtype is inferred from ``out`` tile's dtype. Args: out: Destination Tile (determines target dtype) src: Source Tile mode: Rounding mode — ``pl.RoundMode.CAST_NONE``, ``pl.RoundMode.CAST_RINT``, ``pl.RoundMode.CAST_ROUND``, ``pl.RoundMode.CAST_FLOOR``, ``pl.RoundMode.CAST_CEIL``, ``pl.RoundMode.CAST_TRUNC``, ``pl.RoundMode.CAST_ODD`` """该函数在编译期被前端解析并生成 IR 调用,最终在 NPU 代码生成阶段映射为向量单元的单目转换指令。在 framework/src/codegen/npu/codegen_op_npu.cpp 的unaryOps_映射表中可以看到:
unaryOps_({ // cast op {Opcode::OP_CAST, [this]() { return GenCastOp(); }}, ...即cast在硬件指令层面与EXP、SQRT、ABS等一元向量算子同级,属于向量(SIMD)计算单元的原生操作。
产品支持情况
| 产品形态 | 支持情况 |
|---|---|
| Ascend 950PR / Ascend 950DT | 支持 |
| Atlas A3 训练系列产品 / Atlas A3 推理系列产品 | 不支持 |
| Atlas A2 训练系列产品 / Atlas A2 推理系列产品 | 不支持 |
使用前请务必确认目标硬件为 Ascend 950 系列。
二、函数原型与参数说明
函数原型
pypto_pro.language.cast( out: Tile, src: Tile, *, mode: RoundMode = pl.RoundMode.CAST_ROUND, ) -> None参数语义
| 参数 | 输入/输出 | 说明 |
|---|---|---|
out | 输出 | 目标操作数,须为 UB Tile,目标数据类型由out.dtype确定。out.valid_shape指定本次转换的有效区域;src必须包含与该区域对应的有效元素,且源、目的 Tile 的形状和布局应满足逐元素对应访问要求。调用完成后,out的有效区域被转换结果覆盖。 |
src | 输入 | 源操作数,须为 UB Tile。其有效区域必须覆盖out.valid_shape指定的转换区域,并保证该区域内的每个元素均可由out中的相同逻辑坐标访问。源、目的 Tile 的物理 shape 和行跨度可以不同,但均须为转换区域提供足够的存储空间。 |
mode | 输入(可选) | pypto_pro.language.RoundMode 枚举类型,必须是编译期可确定的 RoundMode 枚举值。默认值为pypto_pro.language.RoundMode.CAST_ROUND。该参数只在转换路径涉及舍入时影响结果。 |
mode是唯一的关键字参数(*之后),调用时必须使用关键字形式传入,如mode=pl.RoundMode.CAST_RINT。
三、三个容易踩坑的接口使用约束
1. 目标类型不通过参数传入
本接口没有dtype或target_type参数,目标数据类型由预先创建的outTile 确定。这与 Tensor 前端的pypto.cast(src, dtype)风格不同,不能写成:
dst = pypto_pro.language.cast(src, pl.DT_FP32) # 错误写法2. 接口不返回新 Tile
转换结果直接写入out,接口返回None。调用模式是先建 Tile、再调用、后使用:
pl.cast(out_tile, src_tile, mode=pl.RoundMode.CAST_RINT) # 正确3. 同类型转换不是通用拷贝
当前仅支持DT_FP32 -> DT_FP32这一种同类型转换,且该路径执行的是舍入操作而非原值拷贝(即对 FP32 数据按指定模式重新舍入)。其他同类型组合(如 FP16→FP16、INT32→INT32)均不支持。若需要数据搬运,应使用其他拷贝/搬运接口。
四、RoundMode 舍入模式详解
mode参数的类型为pypto_pro.language.RoundMode枚举,其完整定义见 RoundMode 文档,七个枚举值的语义如下:
| 枚举值 | 说明 | 示例 |
|---|---|---|
CAST_NONE | 不显式指定舍入规则,具体转换行为由使用该枚举的接口定义 | - |
CAST_RINT | 舍入到最近值,中间值取偶数(银行家舍入) | 2.5 → 2,3.5 → 4 |
CAST_ROUND | 舍入到最近值,中间值远离零 | 2.5 → 3,-2.5 → -3 |
CAST_FLOOR | 向负无穷方向舍入 | 1.6 → 1,-1.6 → -2 |
CAST_CEIL | 向正无穷方向舍入 | 1.6 → 2,-1.6 → -1 |
CAST_TRUNC | 向零方向舍入 | 1.6 → 1,-1.6 → -1 |
CAST_ODD | 发生精度丢失时,将结果的最低有效位设为 1 | DT_FP32 转 DT_FP16:1.0001 → 1.0009765625 |
CAST_RINT与CAST_ROUND的区别在于中间值(恰好在两个可表示值正中间)的处理方向:CAST_RINT取偶数,CAST_ROUND远离零。CAST_TRUNC与CAST_FLOOR在正数域行为一致,但在负数域不同(-1.6 分别为 -1 与 -2)。
cast 接口特有的舍入规则
- 不涉及舍入的路径(类型扩展、整数窄化):
mode不参与结果计算,建议使用CAST_NONE表达意图。 DT_FP16/DT_FP32 -> DT_HF8路径:固定采用CAST_ROUND,传入其他mode不会改变该路径的舍入行为。CAST_ODD的适用范围:除DT_FP32 -> DT_FP16外,对其他路径传入CAST_ODD不会执行奇数舍入,而会回退到CAST_NONE对应的默认规则——浮点数转整数使用CAST_TRUNC,其他路径使用CAST_RINT。
接口默认值与缺省行为
接口缺省传入CAST_ROUND(与 python/pypto_pro/language/_api.py 中的默认参数一致)。但需要注意:缺省值并不等于所有路径都按“中间值远离零”执行,因为各路径对CAST_ROUND的实际处理遵循上节规则。因此,在不需要舍入的扩展/窄化路径上显式传入CAST_NONE是更清晰的表达方式。
五、支持的数据类型转换矩阵(完整版)
下表为源/目的数据类型完整支持组合,未列出的组合不支持。“支持的有效舍入模式”指能够实际改变舍入结果的模式;CAST_NONE可用于所有列出的转换,并按上一节规则处理。
| 源数据类型 | 目的数据类型 | 支持的有效舍入模式 | 特殊说明 |
|---|---|---|---|
| DT_FP32 | DT_FP32 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | 同类型舍入,不是原值拷贝 |
| DT_FP32 | DT_FP16 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC、CAST_ODD | - |
| DT_FP32 | DT_BF16 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | - |
| DT_FP32 | DT_INT16、DT_INT32、DT_INT64 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | CAST_NONE 使用 CAST_TRUNC |
| DT_FP32 | DT_FP8E4M3FN、DT_FP8E5M2 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | - |
| DT_FP32 | DT_HF8 | 固定 CAST_ROUND | 其他 mode 不改变舍入行为 |
| DT_FP16 | DT_FP32 | 不涉及舍入 | 类型扩展,mode 不影响结果 |
| DT_FP16 | DT_INT8、DT_UINT8、DT_INT16、DT_INT32 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | CAST_NONE 使用 CAST_TRUNC |
| DT_FP16 | DT_HF8 | 固定 CAST_ROUND | 其他 mode 不改变舍入行为 |
| DT_BF16 | DT_FP32 | 不涉及舍入 | 类型扩展,mode 不影响结果 |
| DT_BF16 | DT_INT32 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | CAST_NONE 使用 CAST_TRUNC |
| DT_BF16 | DT_FP16 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | - |
| DT_BF16 | DT_FP4E2M1、DT_FP4E1M2 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | FP4 为打包类型,两个元素共用一个字节 |
| DT_FP4E2M1、DT_FP4E1M2 | DT_BF16 | 不涉及舍入 | 类型扩展,mode 不影响结果 |
| DT_UINT8 | DT_FP16、DT_UINT16 | 不涉及舍入 | 类型扩展,mode 不影响结果 |
| DT_INT8 | DT_FP16、DT_INT16、DT_INT32 | 不涉及舍入 | 类型扩展,mode 不影响结果 |
| DT_INT16 | DT_FP16 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | - |
| DT_INT16 | DT_FP32、DT_UINT32、DT_INT32 | 不涉及舍入 | 类型扩展,mode 不影响结果 |
| DT_INT16 | DT_UINT8 | 不涉及舍入 | 整数窄化,mode 不影响结果 |
| DT_INT32 | DT_FP32 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | - |
| DT_INT32 | DT_INT64 | 不涉及舍入 | 类型扩展,mode 不影响结果 |
| DT_INT32 | DT_UINT8、DT_INT16、DT_UINT16 | 不涉及舍入 | 整数窄化,mode 不影响结果 |
| DT_UINT32 | DT_UINT8、DT_UINT16、DT_INT16 | 不涉及舍入 | 整数窄化,mode 不影响结果 |
| DT_INT64 | DT_FP32 | CAST_RINT、CAST_ROUND、CAST_FLOOR、CAST_CEIL、CAST_TRUNC | - |
| DT_INT64 | DT_INT32 | 不涉及舍入 | 整数窄化,mode 不影响结果 |
| DT_FP8E4M3FN、DT_FP8E5M2、DT_HF8 | DT_FP32 | 不涉及舍入 | 类型扩展,mode 不影响结果 |
读取矩阵的三个要点
- “不涉及舍入”列:类型扩展(如 FP16→FP32、INT8→INT16)与整数窄化(如 INT16→UINT8、INT32→INT16)不产生精度舍入,任何
mode均不影响结果。 - 浮点→整数列:当使用
CAST_NONE(或传入无效的CAST_ODD回退)时,浮点转整数默认使用CAST_TRUNC(向零截断),这是最容易忽略的默认行为。 - FP8/FP4 系列:
DT_FP8E4M3FN、DT_FP8E5M2与DT_HF8是三种不同的 8 位浮点格式,DT_FP4E2M1、DT_FP4E1M2为 4 位浮点格式,各有独立的转换路径。
六、其他约束:存储、地址与编译期要求
UB 存储与 valid_shape 约束
out和src必须均为 UB Tile。转换范围由out.valid_shape确定,src的有效区域必须覆盖该范围;源、目的 Tile 的物理 shape 和行跨度可以不同,但必须保证转换区域内的元素能够按相同逻辑坐标逐元素对应。接口只定义out有效区域内的转换结果,不应依赖该区域之外的内容。- 若使用
valid_shape局部转换,务必保证src对应区域的数据已就绪(通常先执行pl.load再执行pl.cast)。
原地转换与地址重叠
DT_FP32 -> DT_FP16支持out和src使用完全相同的 UB 起始地址进行原地转换。- 除该场景外,不保证源、目的存储区域发生部分重叠或完全重叠时的转换结果;使用其他数据类型组合时,应为
out和src分配互不重叠的 UB 存储区域。
FP4 打包类型的 shape 要求
对DT_FP4E2M1和DT_FP4E1M2:
- Tile 的 shape 按逻辑元素计数,两个相邻元素打包在一个字节中,因此物理 shape 的最后一维必须为 2 的倍数;
- 执行
DT_BF16 -> FP4转换时,valid_shape的最后一维也应为偶数,以保证每个有效元素均能组成完整的打包字节。
mode 必须是编译期常量
mode必须在编译期确定,不能使用运行期 Scalar 或 Tensor 值动态选择。这保证了编译器可以在代码生成阶段将舍入模式直接编码进OP_CAST指令属性中(参见 codegen_op_npu.cpp 的GenCastOp分发逻辑)。
七、调用示例
7.1 完整内核:FP16→FP32 类型扩展
以下示例展示了在一个@pl.jit内核中完成从 GM(Global Memory)加载 FP16 数据、转换为 FP32 并写回的完整流程:
import pypto_pro.language as pl @pl.jit(auto_mutex=True) def cast_kernel( src: pl.Tensor[[64, 128], pl.DT_FP16], out: pl.Tensor[[64, 128], pl.DT_FP32], ): src_type = pl.TileType( shape=[64, 128], dtype=pl.DT_FP16, target_memory=pl.MemorySpace.Vec, ) out_type = pl.TileType( shape=[64, 128], dtype=pl.DT_FP32, target_memory=pl.MemorySpace.Vec, ) src_tile = pl.make_tile_group(type=src_type, addrs=0x0000, mutex_ids=[0]) out_tile = pl.make_tile_group(type=out_type, addrs=0x4000, mutex_ids=[1]) with pl.section_vector(): src_current = src_tile.current() out_current = out_tile.current() pl.load(src_current, src, [0, 0]) pl.cast(out_current, src_current, mode=pl.RoundMode.CAST_NONE) pl.store(out, out_current, [0, 0])代码要点:
- Tile 声明:
src_type与out_type均为target_memory=pl.MemorySpace.Vec(UB 空间),分别以0x0000与0x4000起始地址创建 Tile Group,并通过mutex_ids声明互斥,避免读写冲突; - 转换区域:
src_tile.current()/out_tile.current()取得当前迭代的 Tile 视图,pl.load将 GM 数据载入 UB,随后pl.cast执行转换,最后pl.store写回 GM; - mode 选择:FP16→FP32 是类型扩展、不涉及舍入,因此显式使用
CAST_NONE表达意图,与转换矩阵中“mode 不影响结果”的说明一致。
该内核的典型运行结果(示意,节选):
输入数据src:[[-4 -3.75 -3.5 -3.25 -3 -2.75 -2.5 -2.25 ...], [28 28.25 28.5 28.75 29 29.25 29.5 29.75 ...], [60 60.25 60.5 60.75 61 61.25 61.5 61.75 ...], [92 92.25 92.5 92.75 93 93.25 93.5 93.75 ...], ...] 输出数据out:[[-4 -3.75 -3.5 -3.25 -3 -2.75 -2.5 -2.25 ...], [28 28.25 28.5 28.75 29 29.25 29.5 29.75 ...], [60 60.25 60.5 60.75 61 61.25 61.5 61.75 ...], [92 92.25 92.5 92.75 93 93.25 93.5 93.75 ...], ...]可以看到,FP16 值(如 -4、-3.75)扩展为 FP32 后数值完全一致,符合类型扩展的预期。
7.2 舍入模式选择示例
在实际内核中,pl.cast通常作用于已加载到 UB 的 Tile:
# FP32 -> FP16:舍入到最近值,中间值取偶数。 pl.cast(dst_fp16, src_fp32, mode=pl.RoundMode.CAST_RINT) # FP32 -> INT32:向负无穷方向舍入。 # 例如,1.6转换为1,-1.6转换为-2。 pl.cast(dst_int32, src_fp32, mode=pl.RoundMode.CAST_FLOOR) # FP16 -> FP32:类型扩展,不需要舍入。 pl.cast(dst_fp32, src_fp16, mode=pl.RoundMode.CAST_NONE)选型建议:
- 浮点→浮点降精度(如 FP32→FP16、FP32→BF16):默认
CAST_ROUND即可;若需确定性统计行为可选CAST_RINT(银行家舍入);需要向下/向上取整时选CAST_FLOOR/CAST_CEIL; - 浮点→整数:明确指定
CAST_TRUNC(截断)或CAST_FLOOR/CAST_CEIL(按需定向舍入),避免依赖默认的回退规则; - 需要“远离零”语义(如量化场景)且不关心偶数规则时,
CAST_ROUND是直观选择。
八、相关接口与进一步阅读
cast属于 SIMD-API 中的类型转换(Type Conversion)接口族,同目录下还包含融合转换类接口,便于在向量计算流水线中减少中间 Tile 的读写:
- 融合算子(定义于 python/pypto_pro/language/_api.py):
add_relu_cast、sub_relu_cast、mul_cast等将计算与转换合并为单条指令; - SIMT 标量场景:
pl.simt.cast(value, dtype, mode=...)提供标量级转换,其参数校验实现在 python/pypto_pro/ir/op/simt_ops.py,注意该接口仅能在@pl.vector_function(mode="simt")函数内使用,且value必须是标量表达式、dtype必须是pl.DT_*值; - 舍入模式枚举的通用语义,参见 RoundMode 文档;
- 更多向量计算接口(
add、mul、exp、relu等)可查阅 SIMD-API 索引。
九、总结
pypto_pro.language.cast是 PyPTO 向量编程中唯一面向 Tile 的逐元素类型转换接口,其核心设计可归纳为三点:目标类型由outTile 决定、结果写回out而非返回新 Tile、舍入行为由编译期常量mode控制。使用时的关键决策路径是:先确认目标硬件为 Ascend 950 系列,再核对目标类型组合是否出现在支持矩阵中,然后根据转换路径(扩展/窄化/降精度/整数化)选择合适的舍入模式,最后为out、src分配满足重叠规则的 UB 存储。掌握上述规则,即可在量化、精度混合计算和算子融合场景中安全高效地完成各类类型转换。
- 人工智能
- 编译器
- 模型编译
- 高性能计算
- 深度学习
- CANN
【免费下载链接】pypto
PyPTO(发音: pai p-t-o):Parallel Tensor/Tile Operation编程范式。
相关推荐
CANN PyPTO 逐元素舍入接口 pypto.round 详解:接口契约、Tensor 编程用法与平台约束
CANN PyPTO 逐元素舍入接口 pypto.round 详解:接口契约、Tensor 编程用法与平台约束 PyPTO(Parallel Tensor/Ti
人工智能编译器模型编译高性能计算深度学习CANNCANN PyPTO CastMode 详解:类型转换舍入模式、默认规则与二进制舍入实现
CANN PyPTO CastMode 详解:类型转换舍入模式、默认规则与二进制舍入实现 导读 CastMode 是 CANN PyPTO 中用于控制数据类型转
人工智能编译器模型编译高性能计算深度学习CANNPyPTO 逐元素 2 的指数运算 exp2:Tensor.exp2 接口、数据类型约束与 TileShape 配置实战
PyPTO 逐元素 2 的指数运算 exp2:Tensor.exp2 接口、数据类型约束与 TileShape 配置实战 PyPTO(Parallel Tens
人工智能编译器模型编译高性能计算深度学习CANN
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考