Guard功能介绍
当前Guard主要基于下面数据结构实现,如下图所示:

主要包括下面几个模块:
Guard宏
为用户提供了几个宏来生成Guard,主要包括以下几种,通过这些宏可以进行Guard校验并生成相应的Guard,头文件所在路径为:
${INSTALL_DIR}/toolkit/tools/msopgen/template/custom_operator_sample/xx/xx/metadef/inc/graph/symbolizer/symbol_checker.h
其中,${INSTALL_DIR}请替换为CANN软件安装后文件存储路径。若安装的Ascend-cann-toolkit软件包,以root安装举例,则安装后文件存储路径为:/usr/local/Ascend/ascend-toolkit/latest。
- 用于分支校验的ExpectGuard宏:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/* 校验表达式e0与e1是否相等, 如果e0的hint值等于e1的hint值,则宏返回true,并且生成e0 == e1的Guard, 反之,则返回false,并生成e0 != e1的Guard */ #define EXPECT_SYMBOL_EQ(e0, e1) /* 校验表达式e0与e1是否不相等, 如果e0的hint值不等于e1的hint值,则宏返回true,并且生成e0 != e1的Guard, 反之,则返回false,并生成e0 == e1的Guard */ #define EXPECT_SYMBOL_NE(e0, e1) /* 校验表达式e0是否小于e1, 如果e0的hint值小于e1的hint值,则宏返回true,并且生成e0 < e1的Guard, 反之,则返回false,并生成e1 <= e0的Guard */ #define EXPECT_SYMBOL_LT(e0, e1) /* 校验表达式e0是否小于等于e1, 如果e0的hint值小于等于e1的hint值,则宏返回true,并且生成e0 <= e1的Guard, 反之,则返回false,并生成e1 < e0的Guard */ #define EXPECT_SYMBOL_LE(e0, e1) /* 校验表达式e0是否大于e1, 如果e0的hint值大于e1的hint值,则宏返回true,并且生成e1 < e0的Guard, 反之,则返回false,并生成e0 <= e1的Guard */ #define EXPECT_SYMBOL_GT(e0, e1) /* 校验表达式e0是否大于等于e1, 如果e0的hint值大于等于e1的hint值,则宏返回true,并且生成e0 >= e1的Guard, 反之,则返回false,并生成e0 < e1的Guard */ #define EXPECT_SYMBOL_GE(e0, e1)
 
- 用于强校验的AssertGuard宏:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/* 强校验表达式e0与e1是否相等, 如果e0的hint值等于e1的hint值,则生成e0 == e1的Guard,并继续往下执行 反之,则报错并终止流程 */ #define ASSERT_SYMBOL_EQ(e0, e1) /* 强校验表达式e0与e1是否不相等, 如果e0的hint值不等于e1的hint值,则生成e0 != e1的Guard,并继续往下执行 反之,则报错并终止流程 */ #define ASSERT_SYMBOL_NE(e0, e1) /* 强校验表达式e0是否小于e1, 如果e0的hint值小于e1的hint值,则生成e0 < e1的Guard,并继续往下执行 反之,则报错并终止流程 */ #define ASSERT_SYMBOL_LT(e0, e1) /* 强校验表达式e0是否小于等于e1, 如果e0的hint值小于等于e1的hint值,则生成e0 <= e1的Guard,并继续往下执行 反之,则报错并终止流程 */ #define ASSERT_SYMBOL_LE(e0, e1) /* 强校验表达式e0是否大于e1, 如果e0的hint值大于e1的hint值,则生成e1 < e0的Guard,并继续往下执行 反之,则报错并终止流程 */ #define ASSERT_SYMBOL_GT(e0, e1) /* 强校验表达式e0是否大于等于e1, 如果e0的hint值大于等于e1的hint值,则生成e1 <= e0的Guard,并继续往下执行 反之,则报错并终止流程 */ #define ASSERT_SYMBOL_GE(e0, e1)
 
StaticCheck工具类
Guard衍生能力中还提供了一些StaticCheck接口,用于判断符号关系,接口如下,头文件所在路径为:
${INSTALL_DIR}/toolkit/tools/msopgen/template/custom_operator_sample/xx/xx/metadef/inc/graph/symbolizer/symbolic_utils.h
其中,${INSTALL_DIR}请替换为CANN软件安装后文件存储路径。若安装的Ascend-cann-toolkit软件包,以root安装举例,则安装后文件存储路径为:/usr/local/Ascend/ascend-toolkit/latest。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19  | class SymbolicUtils { // 基于之前生成的Guard信息判断e1与e2是否相等,仅基于已有Guard做校验,不生成新的Guard,主要用于内存优化等编译态优化时判断使用 static bool StaticCheckEq(const Expression &e1, const Expression &e2); // 基于之前生成的Guard信息判断e1与e2是否不相等,仅基于已有Guard做校验,不生成新的Guard,主要用于内存优化等编译态优化时判断使用 static bool StaticCheckNe(const Expression &e1, const Expression &e2); // 基于之前生成的Guard信息判断e1是否小于e2,仅基于已有Guard做校验,不生成新的Guard,主要用于内存优化等编译态优化时判断使用 static bool StaticCheckLt(const Expression &e1, const Expression &e2); // 基于之前生成的Guard信息判断e1是否小于等于e2,仅基于已有Guard做校验,不生成新的Guard,主要用于内存优化等编译态优化时判断使用 static bool StaticCheckLe(const Expression &e1, const Expression &e2); // 基于之前生成的Guard信息判断e1是否大于e2,仅基于已有Guard做校验,不生成新的Guard,主要用于内存优化等编译态优化时判断使用 static bool StaticCheckGt(const Expression &e1, const Expression &e2); // 基于之前生成的Guard信息判断e1是否大于等于e2,仅基于已有Guard做校验,不生成新的Guard,主要用于内存优化等编译态优化时判断使用 static bool StaticCheckGe(const Expression &e1, const Expression &e2); }  | 
ShapeEnvAttr
ShapeEnvContext是Guard功能的核心模块,主要提供符号hint值的存储、Guard的存储、基于Guard的化简以及符号hint值计算等功能。当对一张图的输入进行符号泛化时,会在图中生成一个ShapeEnvAttr对象,并将泛化的符号与hint值的映射关系存储其中。而ShapeEnvContext则是作为设置ShapeEnvAttr的作用域而存在的对象,用来表示某段代码需要在该ShapeEnvAttr的作用下使用Guard功能。该模块头文件所在路径为:
${INSTALL_DIR}/toolkit/tools/msopgen/template/custom_operator_sample/xx/xx/metadef/inc/graph/attribute_group/attr_group_shape_env.h
其中,${INSTALL_DIR}请替换为CANN软件安装后文件存储路径。若安装的Ascend-cann-toolkit软件包,以root安装举例,则安装后文件存储路径为:/usr/local/Ascend/ascend-toolkit/latest。
伪码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  | class ShapeEnvGuarder { public: explicit ShapeEnvGuarder(ShapeEnvAttr *shape_env_context); ~ShapeEnvGuarder(); ShapeEnvGuarder(const ShapeEnvGuarder &) = delete; ShapeEnvGuarder(const ShapeEnvGuarder &&) = delete; ShapeEnvGuarder &operator=(const ShapeEnvGuarder &) = delete; ShapeEnvGuarder &&operator=(const ShapeEnvGuarder &&) = delete; private: ShapeEnvAttr *origin_context_; // ShapeEnvContext }; void test(ComputeGraphPtr &graph) { auto root_graph = ge::GraphUtils::FindRootGraph(graph); GE_ASSERT_NOTNULL(root_graph); // 设置context ShapeEnvGuarder guarder(root_graph->GetAttrsGroup<ShapeEnvAttr>()); // 需要生成Guard的逻辑 xxxx }  | 
在上述代码中,ShapeEnvGuarder作为ShapeEnvAttr的作用域使用,当Guard对象被创建时,后续的代码都可以基于当前图中的ShapeEnvAttr使用Guard能力,当Guard对象被销毁时,则ShapeEnvAttr的影响结束。
所有Guard的使用都需要在ShapeEnvAttr的作用域中使用,否则将无法享受到Guard的能力,比如当前只有Lowering、融合策略中的CanFuse和符号推导可以享受到Guard能力,而与符号化不在同一个进程内的Codegen和Schedule组件则无法享受Guard能力。
GuardCodegen模块
ShapeEnvAttr模块存储了生成的Guard信息。那么,在执行阶段,如何利用这些Guard信息呢?如前所述,Guard实际上是一系列关系表达式。因此,可以将这些表达式转换为if判断代码,并在执行阶段开始时对符号的值进行一次检查,从而实现Guard检查的功能。为此,我们提供了GuardCodegen模块,用于将Guard转换为一段检查代码。如下图所示,图中的guard_eq(s0, s1)被转化为了if (!(s0 == s1)) { return false; }语句,当此处if判断为true时,表示Guard不满足,需要进行模型重编译。
