CANN PyPTO 数据类型字节数查询:pypto.bytes_of 用法、返回值与底层实现解析
【免费下载链接】pyptoPyPTO(发音: pai p-t-o):Parallel Tensor/Tile Operation编程范式。项目地址: https://gitcode.com/cann/pypto
导读
在基于 CANN PyPTO(Parallel Tensor/Tile Operation 编程范式)开发算子时,无论是手工分配 Host 内存、计算 Tensor 总字节数,还是判断向量运算的对齐步长,都绕不开"某个数据类型占几个字节"这一基础问题。pypto.bytes_of正是 PyPTO 提供的专用查询接口:传入一个pypto.DataType,即可获得该类型单个元素的存储字节宽度。本文以 PyPTO 官方 API 文档 pypto.bytes_of 为主线,结合仓库源码与单测用例,完整讲解其函数原型、参数返回值、各数据类型速查表、调用示例,以及从 Python 层到 C++ 层的实现原理,并展示它在框架内存计算、形状推断中的典型用途。
产品支持情况
pypto.bytes_of在以下产品形态上均受支持(与 DataType 枚举保持一致):
| 产品形态 | 支持情况 |
|---|---|
| Ascend 950PR / Ascend 950DT | 支持 |
| Atlas A3 训练系列产品 / Atlas A3 推理系列产品 | 支持 |
| Atlas A2 训练系列产品 / Atlas A2 推理系列产品 | 支持 |
功能说明与函数原型
pypto.bytes_of的功能非常简单明确:返回某个数据类型所占的 Byte(字节)大小。这里的"大小"指的是该数据类型单个元素在内存中占用的字节宽度,返回值类型为 Python 的int。
函数原型如下:
bytes_of(dtype: pypto.DataType) -> int其中形参dtype的类型是pypto.DataType,即 PyPTO 框架中用于表示 Tensor 数据类型的枚举类。关于DataType枚举所涵盖的全部类型(整数、浮点数、布尔值等)及其字节/位宽定义,可参见 DataType 文档。
参数说明
| 参数名 | 输入/输出 | 说明 |
|---|---|---|
| dtype | 输入 | 需要查看 Byte 大小的数据类型,取值为pypto.DataType枚举或其常量别名(如pypto.DT_FP32)。 |
返回值说明
返回该数据类型所占的 Byte 大小,类型为int。
各数据类型字节数速查表
将文档给出的 DataType 常量定义 与仓库源码 framework/include/tilefwk/data_type.h 中的DATA_TYPE_ALL描述表(DTYPE_DESC(name, byte, bit, is_float, type, cann_type),第二列即字节数)对照,可以得到 PyPTO 全部支持数据类型的字节数速查表:
| 数据类型常量 | 说明 | bytes_of 返回值 |
|---|---|---|
DT_INT4 | 4 位有符号整数(按字节单元存储) | 1 |
DT_INT8 | 8 位有符号整数 | 1 |
DT_INT16 | 16 位有符号整数 | 2 |
DT_INT32 | 32 位有符号整数 | 4 |
DT_INT64 | 64 位有符号整数 | 8 |
DT_FP8 | 8 位浮点数 | 1 |
DT_FP16 | 16 位半精度浮点数 | 2 |
DT_FP32 | 32 位单精度浮点数 | 4 |
DT_BF16 | 16 位 Brain Float 格式 | 2 |
DT_HF4 | 4 位 Half Float 格式(按字节单元存储) | 1 |
DT_HF8 | 8 位 Half Float 格式 | 1 |
DT_UINT8 | 8 位无符号整数 | 1 |
DT_UINT16 | 16 位无符号整数 | 2 |
DT_UINT32 | 32 位无符号整数 | 4 |
DT_UINT64 | 64 位无符号整数 | 8 |
DT_BOOL | 布尔类型 | 1 |
DT_DOUBLE | 64 位双精度浮点数 | 8 |
DT_FP8E4M3 | 8 位浮点数(4 位指数,3 位尾数) | 1 |
DT_FP8E5M2 | 8 位浮点数(5 位指数,2 位尾数) | 1 |
DT_FP8E8M0 | 8 位浮点数(8 位指数,0 位尾数) | 1 |
DT_FP4_E2M1X2 | MXFP4 格式,双元素打包(2 位指数 1 位尾数) | 1 |
DT_FP4_E1M2X2 | MXFP4 格式,双元素打包(1 位指数 2 位尾数) | 1 |
DT_FP4_E2M1 | 4 位浮点数(2 位指数,1 位尾数) | 1 |
DT_FP4_E1M2 | 4 位浮点数(1 位指数,2 位尾数) | 1 |
注意:表中有多类 4 位(4-bit)数据格式(如
DT_INT4、DT_HF4、DT_FP4_E2M1、DT_FP4_E1M2)。它们虽然位宽只有 4 位,但bytes_of返回的存储单元字节数为 1。这与 C++ 层BytesOf的实现语义一致——该函数返回"表示一个元素所用的字节宽度",用于内存单元、对齐与寻址计算,不能直接用来计算打包后的 Tensor 数据总大小(详见 实现原理 与 使用注意事项)。
调用示例
文档给出的最小调用示例如下:
pypto.bytes_of(pypto.DT_FP32)结果示例如下:
输出: 4在实际开发中,更常见的用法是配合其他常量批量查询,或把返回值用于内存分配计算。例如:
import pypto # 单类型查询 print(pypto.bytes_of(pypto.DT_FP32)) # 输出: 4 print(pypto.bytes_of(pypto.DT_INT64)) # 输出: 8 # 批量查询,构造 {类型: 字节数} 映射 dtypes = [pypto.DT_INT8, pypto.DT_FP16, pypto.DT_FP32, pypto.DT_BF16] bytes_map = {dt: pypto.bytes_of(dt) for dt in dtypes} print(bytes_map) # 输出: {DataType.DT_INT8: 1, DataType.DT_FP16: 2, DataType.DT_FP32: 4, DataType.DT_BF16: 2}单测用例验证
仓库的单测 python/tests/ut/interface/test_enum.py 对pypto.bytes_of的全部常见类型返回值做了断言验证,可直接作为权威参考:
assert pypto.bytes_of(pypto.DT_INT4) == 1 assert pypto.bytes_of(pypto.DT_INT8) == 1 assert pypto.bytes_of(pypto.DT_INT16) == 2 assert pypto.bytes_of(pypto.DT_INT32) == 4 assert pypto.bytes_of(pypto.DT_INT64) == 8 assert pypto.bytes_of(pypto.DT_FP8) == 1 assert pypto.bytes_of(pypto.DT_FP16) == 2 assert pypto.bytes_of(pypto.DT_FP32) == 4 assert pypto.bytes_of(pypto.DT_BF16) == 2 assert pypto.bytes_of(pypto.DT_HF4) == 1 assert pypto.bytes_of(pypto.DT_HF8) == 1 assert pypto.bytes_of(pypto.DT_FP8E4M3) == 1 assert pypto.bytes_of(pypto.DT_FP8E5M2) == 1 assert pypto.bytes_of(pypto.DT_FP8E8M0) == 1 assert pypto.bytes_of(pypto.DT_UINT8) == 1 assert pypto.bytes_of(pypto.DT_UINT16) == 2 assert pypto.bytes_of(pypto.DT_UINT32) == 4 assert pypto.bytes_of(pypto.DT_UINT64) == 8 assert pypto.bytes_of(pypto.DT_BOOL) == 1 assert pypto.bytes_of(pypto.DT_DOUBLE) == 8实现原理:Python 层到 C++ 层的完整调用链
pypto.bytes_of虽然对外是一个纯 Python 接口,但其实际计算发生在 C++ 侧。从源码结构看,完整调用链分为三层:
1. Python 封装层
接口定义位于 python/pypto/_utils.py,其 docstring 给出了与官方文档一致的用法说明,函数体则直接透传给底层绑定接口:
def bytes_of(dtype: DataType) -> int: """return the number of bytes of the current datatype Parameters ---------- dtype: pypto.DataType datatype to be determined the number of bytes Returns ------- int: the size of bytes the datatype contains """ # implementation return pypto_impl.BytesOf(dtype)pypto_impl是 C++ 侧通过 pybind11 导出的底层模块,其类型签名声明在 python/pypto/pypto_impl/init.pyi:
def BytesOf(t: DataType) -> int: ...2. pybind11 绑定层
绑定注册位于 python/src/bindings/controller.cpp:
m.def("BytesOf", [](DataType t) { return BytesOf(t); });这里把 C++ 命名空间中的BytesOf函数直接暴露给 Python,接收DataType枚举值,返回其字节数。
3. C++ 核心实现
真正的字节数来源是 framework/include/tilefwk/data_type.h 中的内联函数BytesOf。它通过DTYPE_DESC宏展开DATA_TYPE_ALL表,逐一返回每个类型声明的 byte 字段:
// Returns the byte width used to represent one element. Do not use it to calculate packed tensor data size. inline size_t BytesOf(DataType t) { switch (t) { #define DTYPE_DESC(name, byte, bit, is_float, type, cann_type) \ case name: \ return byte; DATA_TYPE_ALL #undef DTYPE_DESC default: throw std::invalid_argument("Unknown DataType"); } }值得留意的是注释中的关键约束:"Returns the byte width used to represent one element. Do not use it to calculate packed tensor data size."(返回表示一个元素所用的字节宽度,不要用它计算打包后的 Tensor 数据大小)。这正是 4-bit 类型返回 1 而非 0.5 的原因——该值代表寻址/存储单元宽度,而非严格的位宽换算结果。
如果确实需要计算"元素个数 × 位宽换算出的数据大小",同头文件还提供了DataSizeOf(numel, t)(framework/include/tilefwk/data_type.h),其定义为numel * BitsOf(t) / 0x8,与BitsOf配合使用:
// The caller must ensure that the total number of bits is byte aligned. inline int64_t DataSizeOf(int64_t numel, DataType t) { return numel * BitsOf(t) / 0x8; }在框架中的典型应用场景
BytesOf(以及 Python 侧的bytes_of)在 PyPTO 框架内部被大量使用,主要集中在以下几类场景,理解这些用法有助于在自定义算子开发中复用同样的计算模式:
1. 内存大小计算
最直接的用途是"单元素字节数 × 元素个数 = 数据总字节数"。例如 framework/src/interface/interpreter/communication.cpp 中计算通信数据槽大小:
size_t slotSize = BytesOf(dataType) * std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>());同理,framework/src/cost_model/simulation/arch/PipeSimulatorFast.cpp 在性能仿真中通过shapeSize * BytesOf(dtype)计算访问总字节数。
2. 对齐与 tile 形状推断
在向量/矩阵运算中,常常需要"每多少个元素能凑满一个固定字节块",此时用BLOCK_SIZE / BytesOf(dtype)即可求出。例如 framework/src/passes/tensor_graph_pass/cube_tile_setting.cpp:
uint64_t elePerRepeat = BYTES_PER_REPEAT / BytesOf(static_cast<DataType>(inputType));framework/src/interface/operation/vector/binary_scalar.cpp 中同样以BLOCK_SIZE / BytesOf(...)计算对齐后的元素数。
3. 数据类型转换时的形状换算
在 cast、pack 等涉及 dtype 变换的算子中,需要通过字节数换算形状。例如 framework/src/interface/operation/operation_impl.cpp 中计算目标 Tensor 尾部维度扩展倍数:
float factor = (float)BytesOf(originDType) / (float)BytesOf(dstDataType); auto changedDim = validShape[validShape.size() - 1] * BytesOf(originDType) / BytesOf(dstDataType);4. 内存复用合法性与约束校验
在内存复用 Pass 中,会先比较源/目标缓冲区的元素字节数是否一致,以判断复用是否合法,如 framework/src/passes/block_graph_pass/memory_reuse/merge_src_dst_buffer.cpp。
使用注意事项
- 返回的是存储单元字节数,不是严格位宽:对 4-bit 类型(
DT_INT4、DT_HF4、DT_FP4_E2M1、DT_FP4_E1M2),bytes_of返回 1(存储/寻址单元),而不是 0.5。若需要按位宽精确换算数据总字节数,应使用 C++ 层的BitsOf+DataSizeOf语义。 - 输入必须是合法
DataType:dtype参数类型为pypto.DataType。底层 C++ 实现对未知类型会抛出std::invalid_argument,因此请从 python/pypto/enum.py 导出的常量别名(如DT_FP32、DT_INT32)或DataType枚举成员中取值。 - 产品差异:不同硬件产品支持的
DataType集合有差异。根据 DataType 文档,Atlas A2/A3 系列产品不支持DT_FP8E4M3、DT_FP8E5M2、DT_FP8E8M0,Ascend 950PR/Ascend 950DT 支持全部类型。在目标产品上使用前请先确认类型可用性。 - 常用类型可直接记忆:1 字节(
DT_INT8/DT_UINT8/DT_BOOL/8 位浮点类)、2 字节(DT_INT16/DT_FP16/DT_BF16)、4 字节(DT_INT32/DT_FP32)、8 字节(DT_INT64/DT_UINT64/DT_DOUBLE),与主流计算框架的 sizeof 语义一致,便于日常心算校验。
相关文档
- pypto.bytes_of 官方 API 文档
- DataType 枚举与常量定义
- Tensor API 数据类型模块索引
- Tensor API 总索引
【免费下载链接】pyptoPyPTO(发音: pai p-t-o):Parallel Tensor/Tile Operation编程范式。项目地址: https://gitcode.com/cann/pypto
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考