---
title: TIK表达式
description: "本节主要介绍TIK中表达式即Expr的概念和相关技巧。"
url: https://www.hiascend.com/document/detail/zh/canncommercial/latest/others/tbeaicpudevg/atlasopdev_10_0055.html
sourcePath: /source/zh/canncommercial/900/others/tbeaicpudevg/atlasopdev_10_0055.html
indexId: 48754c564d1f9f53b15870656c6542928455a0f410b7328bb44dd34b0ffa0aff73
---
# TIK表达式

#### 什么是TIK表达式

本节主要介绍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的前端表达的时候，有些细节还是需要注意，下面对一些使用要点进行介绍。


#### 分例1

```
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类型。


如果例子这么写：

#### 分例2

```
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对应的计算。


#### 分例3

除了赋值语句，我们还可以用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的改写也能达到一样的效果。


#### 分例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)
```

为了更加直观地理解表达式和赋值语句之间的区别，我们再来看一个例子：


#### 分例5

```
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前端表达在使用上的区别：

1. scalar表达

```
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)
```


2. expr表达

```
model_point_repeat = init_value=model_point_num // VLENFP32
model_point_residual = init_value=model_point_num % VLENFP32
```


【参考答案】：

1. 第一种是赋值表达，在生成的CCE中会有一个reg_buf去存储model_point_num // VLENFP32的计算量，这样在TIK前端表达中，后面用到model_point_repeat的地方实际上都是用的这个reg_buf寄存器，相当于每次调用存储在这个scalar寄存器中的数值。
2. 第二种是表达式的方式，其实在运行到该语句的时候，并没有进行repeat和residual的计算，而是在后续用到这个expr时（赋值语句、计算语句等）才去实例化计算这个表达式的具体数值，这样的具体数值取决于表达式中各个变量当时的数值，如果开发者在编码过程中改变了表达式中某个变量的值，那么后续的值都会发生变化，可能会有意想不到的bug出现。
3. 前一种表达会多使用Scalar寄存器，但是能够保证每次用到的数值不会改变（除非重新给它赋值），所以在实际的编码场景中，如果变量只需要用到一次，如repeat一般作为for_range语句的extent，而在别的地方不会使用，则可以直接用expr方式进行表达，如果变量需要多次用到，而且在过程中可能涉及到修改，则用scalar方式表达，可以保证数值的正确性并有效地减少重复的计算。
