CANN opbase 未知秩判断接口 IsUnknownRank 使用指南:图模式动态 Shape 推导实战
【免费下载链接】opbase本项目是CANN算子库的基础框架库,为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase
导读
在 CANN opbase(算子库基础框架库)的图模式(Graph 模式)推理与训练场景中,算子输入 Shape 可能处于**未知秩(Unknown Rank)**状态,此时形状推导逻辑必须能够识别这种状态并正确向下游传播。Ops::Base::IsUnknownRank正是 opbase 在 infershape 工具集 中提供的用于检测 Shape 是否为未知秩的基础接口。阅读本文后,你将掌握该接口的函数原型、判定规则与底层实现原理,并能在算子 InferShape 函数中配合SetUnknownRank正确完成未知秩状态的传递。
功能说明
IsUnknownRank用于图模式场景下检查输入 Shape 是否为未知秩。
在动态 Shape 支持的图执行流程中,算子 Shape 推导(InferShape)可能在编译期无法确定张量的维度个数。此时该 Shape 被称为"未知秩",其内部以特殊标记值表示。opbase 将这类 Shape 的判定与设置封装成独立工具接口,供各算子的 InferShape 实现复用,避免各算子重复实现相同的判读逻辑。
从命名空间看,该接口位于Ops::Base命名空间(即 opbase 的公共基础工具层),同族接口还包括:
Ops::Base::SetUnknownRank:将 Shape 置为未知秩,与IsUnknownRank配对使用;Ops::Base::IsUnknownShape/SetUnknownShape:处理已知秩、但某些维度未知(-1)的 Shape,与未知秩(-2)是两种不同的动态形态。
函数原型
bool IsUnknownRank(const gert::Shape &shape)其中gert::Shape是图引擎运行时(Graph Engine Runtime)提供的 Shape 类型,opbase 侧在 shape_util.h 中通过#include "exe_graph/runtime/shape.h"引入该类型定义。
参数说明
| 参数名 | 输入/输出 | 说明 |
|---|---|---|
| shape | 输入 | 待检查的输入 Shape。 |
参数以const引用方式传入,函数不会修改入参 Shape 内容,仅执行只读判定。
返回值说明
返回类型为bool:
true:输入 shape 为未知秩;false:输入 shape 不为未知秩(即维度个数已确定)。
判定规则与底层实现
该接口的判定规则直接体现在源码中,位于 shape_util.cpp:
namespace Ops { namespace Base { constexpr int64_t UNKNOWN_RANK_DIM_VALUE = -2LL; constexpr int64_t UNKNOWN_DIM_VALUE = -1LL; void SetUnknownRank(gert::Shape& shape) { shape.SetDimNum(0); shape.AppendDim(UNKNOWN_RANK_DIM_VALUE); } bool IsUnknownRank(const gert::Shape& shape) { return shape.GetDimNum() == 1 && shape.GetDim(0) == UNKNOWN_RANK_DIM_VALUE; } } // namespace Base } // namespace Ops从实现可以提炼出两条关键事实:
- 未知秩的编码约定:opbase 使用常量
UNKNOWN_RANK_DIM_VALUE = -2(即-2LL)作为未知秩的标记值;与之相对,UNKNOWN_DIM_VALUE = -1表示"秩已知但某一维未知"。二者含义不同,切勿混用。 - 判定条件:
IsUnknownRank要求 Shape 满足维度个数为 1 且唯一维度取值为 -2,即内部表示形如(-2,)。之所以要求"1 维 + 值 -2",是为了与"正常的一维 Shape(如(5,))"以及"未知维 Shape"在数值表示上严格区分,避免误判。
同时可以看到,未知秩 Shape 由配套的SetUnknownRank构造:先将维度数清零,再追加一个值为-2的维度,最终得到(-2,)的形态,与IsUnknownRank的判定条件一一对应。头文件中的对外声明位于 shape_util.h,并使用OPBASE_API导出宏保证跨库符号可见。
调用示例
官方文档给出的关键代码如下,仅供参考,不支持直接拷贝运行(需结合具体算子的 InferShape 上下文):
auto in_shape = context->GetInputShape(0); // 0表示第一个输入参数 OP_CHECK_NULL_WITH_CONTEXT(context, in_shape); auto out_shape = context->GetOutputShape(0); OP_CHECK_NULL_WITH_CONTEXT(context, out_shape); // 判断输入张量shape是否为未知秩,若是,将输出张量shape置为未知秩 if (Ops::Base::IsUnknownRank(*in_shape)) { Ops::Base::SetUnknownRank(*out_shape); }代码要点:
GetInputShape(0)/GetOutputShape(0)取自gert::InferShapeContext,分别表示第一个输入与第一个输出的 Shape;OP_CHECK_NULL_WITH_CONTEXT是 opbase 提供的空指针校验宏,失败时携带算子上下文信息打印日志并终止推导;- 判定通过后,必须调用
Ops::Base::SetUnknownRank(*out_shape)将输出 Shape 也置为未知秩,否则输出形状信息会与实际推导状态不一致。
源码级应用佐证:未知秩在公共推导流程中的传播
IsUnknownRank并非孤立接口,opbase 内置的多个公共 InferShape 工具在实现中都直接复用了该判定,可作为真实调用链的参考。
逐元素算子推导 InferShape4Elewise
在 infershape_elewise_util.cpp 中,InferShape4Elewise的处理逻辑是:
ge::graphStatus InferShape4Elewise(gert::InferShapeContext* context) { auto inShape = context->GetInputShape(0); OP_CHECK_NULL_WITH_CONTEXT(context, inShape); auto outShape = context->GetOutputShape(0); OP_CHECK_NULL_WITH_CONTEXT(context, outShape); if (IsUnknownRank(*inShape)) { OP_LOGD(context->GetNodeName(), "input shape is UnknownRank, set output shape to (-2, )"); SetUnknownRank(*outShape); return ge::GRAPH_SUCCESS; } *outShape = *inShape; return ge::GRAPH_SUCCESS; }可见逐元素类算子的通用策略是:输入为未知秩时,直接将输出置为未知秩并提前返回;否则输出等于输入 Shape。
广播推导 BroadcastShapeToOutShape
在 infershape_broadcast_util.cpp 的BroadcastShapeToOutShape中,只要参与广播的两个 Shape 任一为未知秩,输出即被置为未知秩:
if (IsUnknownRank(*shape) || IsUnknownRank(*shapeOutput)) { OP_LOGD("BroadcastShapeToOutShape", "the input shape is [-2], set output shape is [-2]!"); SetUnknownRank(*shapeOutput); return true; }这两个示例说明:未知秩具有"传染性"——只要上游输入处于未知秩状态,任何依赖其形状的推导结果都无法在编译期确定,必须向下游持续传递(-2,)标记,直至运行时 Shape 信息恢复。
与未知维 Shape 的区分使用
opbase 在 shape_util.h 中还提供了一对针对"未知维"的接口:
OPBASE_API void SetUnknownShape(int64_t rank, gert::Shape& shape); OPBASE_API bool IsUnknownShape(const gert::Shape& shape);两者适用场景存在本质区别,使用时请务必区分:
| 接口 | 标记值 | 含义 | 典型场景 |
|---|---|---|---|
IsUnknownRank/SetUnknownRank | -2 | 维度个数(秩)未知 | 图模式下连 Rank 都无法静态确定 |
IsUnknownShape/SetUnknownShape | -1 | 秩已知,但部分维度大小未知 | 动态 Batch、动态序列长度等仅个别维未知 |
在 shape_util.cpp 的实现中,SetUnknownShape要求入参rank >= 0(否则记录错误日志并直接返回),随后将指定个数的维度全部置为-1;IsUnknownShape则遍历所有维度,只要存在任一-1即返回true。可以看到这两套接口内部使用完全不同的标记值,判读逻辑也因此截然不同。
约束与注意事项
- 官方文档声明本接口无额外约束,可安全用于图模式 InferShape 流程;
- 该接口仅做 Shape 状态判定,本身不触发错误、不分配资源,可在推导路径中高频调用;
- 调用前应确保传入的
gert::Shape有效(如已通过OP_CHECK_NULL_WITH_CONTEXT校验指针非空); - 判定的前提是 Shape 采用 opbase 约定的
-2编码约定(UNKNOWN_RANK_DIM_VALUE),若 Shape 来源于其他框架或自定义构造,请先确认其编码方式一致; - 在需要同时处理"未知秩"与"未知维"的场景(例如融合算子推导),建议分别调用
IsUnknownRank与IsUnknownShape组合判断,避免把-2与-1两种状态混为一谈。
延伸阅读
- SetUnknownRank 接口文档:与本文接口配对的写入接口,用于将输出 Shape 置为未知秩;
- infershape 工具集索引:查看更多 Shape 推导基础接口(如
IsUnknownShape、InferShape4Broadcast、InferShape4Elewise等); - shape_util.h:全部 Shape 工具接口的声明与导出;
- shape_util.cpp:
-2/-1标记常量及全部工具接口的实现; - infershape_elewise_util.cpp:逐元素推导中未知秩传播的完整调用示例;
- infershape_broadcast_util.cpp:广播推导中未知秩传播的完整调用示例。
【免费下载链接】opbase本项目是CANN算子库的基础框架库,为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考