本节主要介绍TIK中表达式即Expr的概念和相关技巧。
表达式(expression)是值、TIK变量(Tensor、Scalar、InputScalar)和运算符的组合。 值自身也被认为是一个表达式,变量也是。例如:
# 变量 a # 立即数 1 # 计算式 b * 3 + 10 # 逻辑式 e <= f # 复杂式 # 如for_range里的循环变量i,j等 (i * 256) + (j * 16) + 8
在TIK前端表达中,变量(Variable),立即数(Int、Float、String),式子中包含逻辑操作符(And、Or、Not、Add、Sub、Mul、Div、Mod、FloorDiv、FloorMod、Min、Max、EQ、NE、LT、LE、GT、GE)等都可以视为是表达式Expr。
但是读者在写TIK的前端表达的时候,有些细节还是需要注意,下面对一些使用要点进行介绍。
a_scalar = tik_instance.Scalar("int32", "a_scalar", init_value=0) b_scalar = tik_instance.Scalar("int32", "b_scalar", init_value=0) c_scalar = a_scalar + b_scalar >>> print(type(a_scalar)) <class 'tbe.tik.api.tik_scalar.Scalar'> >>> print(type(c_scalar)) <class 'tbe.tik.tik_lib.tik_expr.Expr'>
这里定义了两个Scalar变量:a_scalar和b_scalar,然后将a_scalar和b_scalar相加,想得到c_scalar,我们通过打印其类型,会发现两个Scalar类型的变量,加起来的类型是Expr类型,并不是Scalar类型。
如果例子这么写:
a_scalar = tik_instance.Scalar("int32", "a_scalar", init_value=0) b_scalar = tik_instance.Scalar("int32", "b_scalar", init_value=0) c_scalar = tik_instance.Scalar("int32", "c_scalar", init_value=a_scalar+b_scalar) >>> print(type(a_scalar)) <class 'tbe.tik.api.tik_scalar.Scalar'> >>> print(type(c_scalar)) <class 'tbe.tik.api.tik_scalar.Scalar'>
那c_scalar就会变成Scalar类型(理当如此,直接用构造函数构造了一个Scalar实例了)。
分例1中,TIK不会返回Tensor或者Scalar,返回的是Expr,Expr不会被编译生成CCEC,即实际上并没有对c_scalar赋值。
分例2中,使用了Scalar的赋值语句来实例化Expr对应的计算,将Expr作为操作数赋值给了新的Scalar变量,并会被编译生成CCEC。
当且仅当Expr作为赋值语句的操作数的时候才会实例化Expr对应的计算。
除了赋值语句,我们还可以用TIK API的Scalar管理语句set_as来完成对Expr操作数的实例化:
a_scalar = tik_instance.Scalar("int32", "a_scalar", init_value=0) b_scalar = tik_instance.Scalar("int32", "b_scalar", init_value=0) c_scalar = tik_instance.Scalar("int32", "c_scalar", init_value=0) # 1. set_as可以直接接受Expr类型 c_scalar.set_as(a_scalar + b_scalar)
同样分例4的改写也能达到一样的效果。
a_scalar = tik_instance.Scalar("int32", "a_scalar", init_value=0) b_scalar = tik_instance.Scalar("int32", "b_scalar", init_value=0) c_scalar = tik_instance.Scalar("int32", "c_scalar", init_value=0) # 2. set_as也可以接受Scalar类型 c_expr = a_scalar + b_scalar c_scalar.set_as(c_expr)
为了更加直观地理解表达式和赋值语句之间的区别,我们再来看一个例子:
a_scalar = tik_instance.Scalar("int32", "a_scalar", init_value=0) b_scalar = tik_instance.Scalar("int32", "b_scalar", init_value=0) c_expr = a_scalar + b_scalar d_scalar = tik_instance.Scalar("int32", "d_scalar", init_value=c_expr) a_scalar.set_as(a_scalar + 1) e_scalar = tik_instance.Scalar("int32", "e_scalar", init_value=c_expr)
从TIK前端表达来看,如果不了解c_expr是一个表达式的概念以及它何时实例化,可能错认为d_scalar和e_scalar的值是一样的,实际上,生成的IR是不一样的,还是那句话,只有当Expr作为赋值语句的操作数时,才会实例化Expr对应的计算。所以在实际的赋值语句中,出现c_expr的地方会被实例化成a_scalar + b_scalar。
从另一个方面也可以认为c_expr是一个a_scalar + b_scalar的别名。
TIK在前端处理的时候,不会将表达式Expr生成IR,遇到赋值语句的时候才会生成IR。
a_scalar = tik_instance.Scalar("int32", "a_scalar", init_value=0) # 生成IR b_scalar = tik_instance.Scalar("int32", "b_scalar", init_value=0) # 生成IR c_expr = a_scalar + b_scalar # 不生成IR d_scalar = tik_instance.Scalar("int32", "d_scalar", init_value=c_expr) # 生成IR a_scalar.set_as(a_scalar + 1) # 生成IR e_scalar = tik_instance.Scalar("int32", "e_scalar", init_value=c_expr) # 生成IR
请说明以下两个TIK前端表达在使用上的区别:
model_point_repeat = tik_instance.Scalar("int16", init_value=model_point_num // VLENFP32) model_point_residual = tik_instance.Scalar("int16", init_value=model_point_num % VLENFP32)
model_point_repeat = init_value=model_point_num // VLENFP32 model_point_residual = init_value=model_point_num % VLENFP32
【参考答案】: