
CANN opbase 算子开发指南PromoteType 数据类型提升判定【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase导读在 CANN opbase 算子库中二元/多元算子如 Add、Mul 等经常遇到两个输入张量数据类型不一致的情况此时需要先确定“以何种数据类型统一计算”再决定是否插入 Cast 转换。PromoteType正是 opbase 为算子开发者提供的类型提升type promotion判定工具函数它接收两个ge::DataType返回二者运算时应统一提升到的目标数据类型若组合不合法则返回DT_UNDEFINED。读完本文你将掌握PromoteType的完整语义、全部类型组合的推导规则、底层查找表实现原理以及如何在算子 Host 侧代码中用它做前置合法性校验与 Cast 决策。功能说明为什么算子需要类型提升不同数据类型参数进行运算时推导应该将类型提升到何种数据类型进行计算。例如float16与float32进行运算应该将float16先提升至float32后再做运算int8与int32运算则应提升至int32。若算子实现中没有统一的类型提升规则同一算子面对不同的输入 dtype 组合就可能产生不一致的中间计算类型进而导致精度或行为差异。PromoteType在 opbase 中承担了这一“类型提升标准答案”的职责它与其姊妹函数CanCast、CheckType、IsFloatingType 等共同构成 data_type_utils 工具族。完整的接口清单可参见 data_type_utils 接口索引。函数原型ge::DataType PromoteType(ge::DataType type_a, ge::DataType type_b)该函数在头文件 include/nnopbase/opdev/data_type_utils.h 中以内联函数形式提供位于op命名空间下无需链接额外库即可直接使用。参数说明参数输入/输出说明type_a输入第一个参数数据类型取值为ge::DataType枚举。type_b输入第二个参数数据类型取值为ge::DataType枚举。ge::DataType枚举覆盖了 opbase 支持的主要数据类型包括DT_FLOATf4、DT_FLOAT16f2、DT_INT8i1、DT_INT16i2、DT_INT32i4、DT_INT64i8、DT_UINT8u1、DT_DOUBLEf8、DT_BOOLb1、DT_BF16bf、DT_COMPLEX32/64/128c2/c4/c8以及DT_HIFLOAT8、DT_FLOAT8_E5M2、DT_FLOAT8_E4M3FN、DT_FLOAT8_E8M0、DT_FLOAT6_E3M2、DT_FLOAT6_E2M3、DT_FLOAT4_E2M1、DT_FLOAT4_E1M2等低比特浮点类型。源码中在 include/nnopbase/opdev/data_type_utils.h 定义了对应的短别名常量如f4、i4、bf查找表即直接引用这些别名。返回值说明返回type_a与type_b运算时应统一提升到的目标ge::DataType若无法推导类型提升后的数据类型运算会失败并返回DT_UNDEFINED当type_a type_b时直接返回该类型本身无需提升当任一入参超出合法枚举范围 0或 DT_MAX时同样返回DT_UNDEFINED边界保护见 include/nnopbase/opdev/data_type_utils.h。类型提升规则速查PromoteType的推导规则完全由一张静态二维查找表kPromoteTypesLookup决定其定义位于 include/nnopbase/opdev/data_type_utils.h。下表整理了常见数据类型的提升结果行/列分别为type_a/type_b组合顺序不影响结果因为查找表对常见类型是镜像对称的type_a \ type_bUINT8INT8INT16INT32INT64FP16FP32BF16DOUBLEBOOLCOMPLEX64UINT8UINT8INT16INT16INT32INT64FP16FP32BF16DOUBLEUINT8COMPLEX64INT8INT16INT8INT16INT32INT64FP16FP32BF16DOUBLEINT8COMPLEX64INT16INT16INT16INT16INT32INT64FP16FP32BF16DOUBLEINT16COMPLEX64INT32INT32INT32INT32INT32INT64FP16FP32BF16DOUBLEINT32COMPLEX64INT64INT64INT64INT64INT64INT64FP16FP32BF16DOUBLEINT64COMPLEX64FP16FP16FP16FP16FP16FP16FP16FP32FP32DOUBLEFP16COMPLEX64FP32FP32FP32FP32FP32FP32FP32FP32FP32DOUBLEFP32COMPLEX64BF16BF16BF16BF16BF16BF16FP32FP32BF16DOUBLEBF16COMPLEX64DOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLEDOUBLECOMPLEX128BOOLUINT8INT8INT16INT32INT64FP16FP32BF16DOUBLEBOOLCOMPLEX64COMPLEX64COMPLEX64COMPLEX64COMPLEX64COMPLEX64COMPLEX64COMPLEX64COMPLEX64COMPLEX64COMPLEX128COMPLEX64COMPLEX64关键规则可归纳为以下几点整数宽度升级不同宽度的整数类型含UINT8与INT8混合提升到能同时容纳两者的更宽整数类型如UINT8 INT8 → INT16INT8 INT32 → INT32整数向浮点提升任意整数与浮点类型运算时提升到浮点类型如INT64 FP16 → FP16UINT8 FP32 → FP32浮点宽度升级FP16 BF16 → FP32FP16/FP32/BF16 DOUBLE → DOUBLE实向复提升实数类型与复数类型运算时提升到复数类型且DOUBLE/COMPLEX64混合时直接升到COMPLEX128如DOUBLE COMPLEX64 → COMPLEX128BOOL 并入其他类型BOOL与数值类型运算时按另一类型提升BOOL UINT8 → UINT8BOOL FP32 → FP32不支持的类型组合返回DT_UNDEFINED如QINT8/QINT16/QINT32、DT_STRING、DT_RESOURCE等与任何普通数值类型的组合以及UINT16/UINT32/UINT64与多数类型的组合均返回DT_UNDEFINED表示“该组合不支持类型提升”。源码实现原理三段式实现PromoteType的实现非常精简仅包含三步include/nnopbase/opdev/data_type_utils.hinline ge::DataType PromoteType(ge::DataType type_a, ge::DataType type_b) { if (static_castint(type_a) 0 || static_castint(type_b) 0 || static_castint(type_a) static_castint(ge::DataType::DT_MAX) || static_castint(type_b) static_castint(ge::DataType::DT_MAX)) { return ge::DataType::DT_UNDEFINED; // 1. 边界保护非法枚举直接返回未定义 } if (type_a type_b) { return type_a; // 2. 相同类型无需提升直接返回 } return kPromoteTypesLookup[static_castint(type_a)][static_castint(type_b)]; // 3. 查表推导 }第 1 步的边界检查保证了即使调用方传入未初始化的枚举值如-1也不会越界访问查找表第 2 步是同类型快速路径第 3 步通过两次枚举值到整型的转换从DT_MAX × DT_MAX的二维常量表中取出最终结果。二维查找表kPromoteTypesLookup是一个DT_MAX × DT_MAX的constexpr二维数组include/nnopbase/opdev/data_type_utils.h行、列均按下标对应ge::DataType枚举值例如下标 0 对应DT_FLOAT、1 对应DT_FLOAT16、27 对应DT_BF16、28 对应DT_UNDEFINED。表中绝大多数不支持的组合填入了ud即DT_UNDEFINED因此在编译期即可确定全部组合的行为运行时仅需一次 O(1) 查表零开销、无动态分配非常适合算子 Host 侧高频调用的场景。说明另一命名空间下的同名实现opbase 仓库中还存在一份位于opcommon命名空间的同名实现位于 include/nnopbase/opdev/op_common/data_type_utils.h。该头文件头部带有编译期#warning明确提示其计划于 2027 年 6 月废弃并建议迁移到 CANN 安装路径下include/aclnn/opdev中的同名新接口。因此在编写新算子时应优先使用 include/nnopbase/opdev/data_type_utils.h 中op::PromoteType避免新代码绑定到即将废弃的接口。调用示例示例一校验 dtype 是否可与 Float32 计算原文档示例#include opdev/data_type_utils.h // 校验dtype是否可以与Float32计算不能则提前返回 void Func(const ge::DataType dtype) { if (PromoteType(dtype, DT_FLOAT) DT_UNDEFINED) { return; } // 后续执行算子计算逻辑 }当dtype为DT_STRING、DT_QINT8等与DT_FLOAT无法提升的类型时PromoteType返回DT_UNDEFINED函数提前退出避免后续对非法类型执行计算。示例二获取统一计算类型并指导 Cast 决策#include opdev/data_type_utils.h // 计算两个输入统一计算类型并据此决定是否需要对 type_a 做 Cast 前移 ge::DataType DecideComputeType(const ge::DataType type_a, const ge::DataType type_b) { ge::DataType compute_type PromoteType(type_a, type_b); if (compute_type ge::DataType::DT_UNDEFINED) { // 类型组合不合法交由上层做报错或 fallback 处理 return ge::DataType::DT_UNDEFINED; } // 若 compute_type ! type_a则实现中可对第一个输入插入 Cast 到 compute_type return compute_type; }示例三与 CheckType 组合进行更严格的入参校验#include opdev/data_type_utils.h bool ValidateAndPromote(const ge::DataType type_a, const ge::DataType type_b, ge::DataType compute_type) { // 先限定允许参与计算的类型集合 if (!CheckType(type_a, {ge::DataType::DT_FLOAT, ge::DataType::DT_FLOAT16, ge::DataType::DT_INT32, ge::DataType::DT_BF16}) || !CheckType(type_b, {ge::DataType::DT_FLOAT, ge::DataType::DT_FLOAT16, ge::DataType::DT_INT32, ge::DataType::DT_BF16})) { return false; } compute_type PromoteType(type_a, type_b); return compute_type ! ge::DataType::DT_UNDEFINED; }测试验证仓库为PromoteType提供了系统性的单元测试位于 tests/nnopbase/ut/composite_op/test_data_type_utils.cppST 侧对应用例见 tests/nnopbase/st/composite_op/test_data_type_utils.cpp。测试通过宏TEST_PROMOTE(typeA, typeB, TypeExpected)覆盖了大量类型组合例如TEST_F(TestDataTypeUtils, TestPromoteType) { TEST_PROMOTE(op::DataType::DT_UINT8, op::DataType::DT_UINT8, op::DataType::DT_UINT8); TEST_PROMOTE(op::DataType::DT_UINT8, op::DataType::DT_INT8, op::DataType::DT_INT16); TEST_PROMOTE(op::DataType::DT_UINT8, op::DataType::DT_INT32, op::DataType::DT_INT32); TEST_PROMOTE(op::DataType::DT_UINT8, op::DataType::DT_FLOAT, op::DataType::DT_FLOAT); TEST_PROMOTE(op::DataType::DT_UINT8, op::DataType::DT_COMPLEX128, op::DataType::DT_COMPLEX128); TEST_PROMOTE(op::DataType::DT_UINT8, op::DataType::DT_QINT8, op::DataType::DT_UNDEFINED); TEST_PROMOTE(op::DataType::DT_UINT8, op::DataType::DT_BF16, op::DataType::DT_BF16); TEST_PROMOTE(op::DataType::DT_FLOAT16, op::DataType::DT_FLOAT, op::DataType::DT_FLOAT); TEST_PROMOTE(op::DataType::DT_FLOAT16, op::DataType::DT_BF16, op::DataType::DT_FLOAT); TEST_PROMOTE(op::DataType::DT_DOUBLE, op::DataType::DT_COMPLEX64, op::DataType::DT_COMPLEX128); TEST_PROMOTE(op::DataType::DT_BOOL, op::DataType::DT_FLOAT, op::DataType::DT_FLOAT); TEST_PROMOTE(op::DataType::DT_QINT8, op::DataType::DT_INT8, op::DataType::DT_UNDEFINED); TEST_PROMOTE(op::DataType::DT_QINT8, op::DataType::DT_QINT8, op::DataType::DT_QINT8); }这些用例既验证了常规组合的提升结果也覆盖了QINT系列返回DT_UNDEFINED的负例可作为算子开发者核对“自己关心的类型组合是否受支持”的可靠参考。与同类工具函数的配合使用PromoteType通常与data_type_utils中的其他工具配合构成完整的 dtype 处理链路CheckType校验 dtype 是否属于某个允许的类型集合可在调用PromoteType之前做白名单过滤CanCast判断从from类型能否转换到to类型复数不可转实数、浮点不可转整数、仅 BOOL 可转 BOOL确定提升目标后可用它决定是否执行 CastIsFloatingType / IsIntegralType / IsComplexType分类判断函数CanCast内部即依赖它们TypeSize获取类型字节大小可用于提升后重新计算张量字节数或校验 shapeToString将 dtype 转字符串便于在日志中输出提升结果。使用建议与注意事项优先使用op::命名空间的新接口避免使用带废弃警告的opcommon::版本include/nnopbase/opdev/op_common/data_type_utils.h先做合法性判断再使用返回值PromoteType返回DT_UNDEFINED不代表“目标类型就是 undefined”而是“该组合不支持提升”调用方必须在拿到返回值后显式判断并处理失败分支组合顺序不影响结果查找表对常见数值类型对称PromoteType(a, b) PromoteType(b, a)但为可读性建议调用方统一入参顺序与 CanCast 语义不同PromoteType回答“该以什么类型计算”CanCast回答“能否转换到目标类型”二者一个用于选型、一个用于落地不要混用提升目标未必是“更宽”的类型例如INT64 FP16 → FP16此时整数大数会因转浮点而损失精度这是类型提升的既定语义需要在算子文档中向用户说明。【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考