TIK的作用域即一个TIK变量有效的区域,又可叫生命周期或活跃状态。
一个TIK变量在申请时被创建,至它所在的代码块末尾时被释放,在创建到释放之间处于活跃状态,并且只有在活跃状态时/生命周期/作用域内才能被访问。
在TIK中访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。
Scalar的作用域/生命周期符合如下规则:
Scalar的作用域/生命周期示例如图1所示,该图显示了S0,S1,S2三个变量的活跃状态。
TIK中,定义函数、for循环、if判断等都会引入新的作用域,外部无法访问在它们的作用域内定义的变量,例如:
g_scalar = tik_instance.Scalar(dtype = "int16", init_value = 1) # 全局作用域,g_scalar在全局都能被访问 def tik_func_1(): func_scalar = tik_instance.Scalar(dtype = "int16", init_value = 2) # func_scalar 仅在函数tik_func_1的作用域内被访问,此处可访问g_scalar和func_scalar with tik_instance.if_scope(func_scalar < 5): if_scalar = tik_instance.Scalar(dtype = "int16", init_value = 3) # if_scalar仅在if的作用域内被访问,此处可访问g_scalar,func_scalar和if_scalar with tik_instance.for_range(0, 4) as i: for_scalar = tik_instance.Scalar(dtype = "int16", init_value = 4) # for_scalar仅在for的作用域内被访问,此处可访问g_scalar,func_scalar,if_scalar和for_scalar
Tensor的作用域/生命周期符合如下规则:
Tensor的作用域/生命周期示例如图2所示。
上述示例中,代码被分成了5个时段,分别用1-5表示,每个时段活跃的Tensor以及总共使用的UB大小如表1所示。
时段 |
活跃Tensor |
使用UB总大小 |
---|---|---|
1 |
B0 |
256*2 Byte |
2 |
B0,B1 |
256*2*2 Byte |
3 |
B0,B1,B2 |
256*3*2 Byte |
4 |
B0,B1,B3 |
256*3*2 Byte |
5 |
B0,B4 |
256*2*2 Byte |
实际开发活动中,输入张量shape可能超过Unified Buffer容量上限,必须经过多次传输才能完成计算;此时,为最大化利用Unified Buffer存储空间,数据定义指定的shape大小一般为Unified Buffer允许的最大值。代码示例如下:
# 获取Unified Buffer空间大小,单位为Byte ub_size_bytes = tbe.common.platform.get_soc_spec("UB_SIZE") # 例如:unified buffer空间大小为:128 Bytes # Unified Buffer上数据读取和写入必须32 Bytes对齐,一个block的大小为32 Bytes block_byte_size = 32 # 根据输入的数据类型dtype_x,计算一个block可以存放多少个对应的元素 def get_bit_len(dtype): index = 0 for i in dtype: if i.isdigit(): break index += 1 return int(dtype[index:]) dtype_bytes_size = get_bit_len(dtype_x) // 8 # 将bit换算成byte,输入数据类型为int16,一个int16的数为 2 Bytes data_each_block = block_byte_size // dtype_bytes_size # 则一个block可以放 32/2=16 个元素 # 计算在Unified Buffer上需要分别分配多少空间,并进行32Bytes对齐 ub_tensor_size = (ub_size_bytes // dtype_bytes_size // # 则 ub_tensor_size = 128 // 2 // 16 * 16 = 64 data_each_block * data_each_block) # 即 最多定义包含64个int16的元素的Tensor # 在Unified Buffer上创建tensor input_x_ub input_x_ub = tik_instance.Tensor(dtype_x, (ub_tensor_size,), name="input_x_ub", scope=tik.scope_ubuf)
另外,用户也可通过地址复用(或称地址重叠)以节省Unified Buffer存储空间。以矢量单目运算为例,当地址复用满足相应约束时,用户可以定义一个Tensor供源操作数与目的操作数同时使用,此时能节省一倍的存储空间。
''' 示例: tensor_a 既是源操作数,也是目的操作数 下面这条语句将tensor_a的值修改为它的绝对值: before: tensor_a = [-3, -2, -1, 0, 1, 2, 3, ...] after: tensor_a = [3, 2, 1, 0, 1, 2, 3, ...] ''' tensor_a = tik_instance.Tensor("int16", (128,), name="tensor_a", scope=tik.scope_ubuf) tik_instance.vec_abs(128, tensor_a, tensor_a, 1, 8, 8)
需特别注意因地址对齐导致的超出对应内存类型的总容量时,会引起编译报错。
例如:在下图示例中,假设UB的空间大小为1024B(B为Byte的简写),申请两个UB Buffer的tensor_a和tensor_b,大小分别为1008B和16B,内存分配时按照32Byte对齐要求向上对齐到1024B和32B,实际要求内存共1056B,大于UB Buffer的内存总容量1024B。
用户定义的Tensor在内存分配时会对起始地址进行对齐,不同scope的对齐要求如下表:
scope |
对齐要求 |
---|---|
Unified Buffer |
Atlas 200/300/500 推理产品,要求32Byte对齐 Atlas 训练系列产品,要求32Byte对齐 Atlas推理系列产品AI Core,要求32Byte对齐 Atlas推理系列产品Vector Core,要求32Byte对齐 Atlas A2训练系列产品,要求32Byte对齐 |
L1 Buffer |
512Byte对齐 |
L1OUT Buffer |
float16类型数据要求512Byte对齐;float32/int32/uint32类型数据要求1024Byte对齐 |
Global Memory |
暂无对齐要求 |
使用TIK数据计算和数据搬运接口时,目的操作数和源操作数地址偏移对齐要求和上表保持一致,如果TIK指令接口中已说明操作数起始地址对齐要求,则以具体指令中的说明为准。