TIK Data Definition
Basic Data Types
- A data type is an attribute of data which tells the compiler or interpreter how the programmer intends to use the data. Basic data types include int8, uint8, int16, uint16, int32, uint32, float16, float32, and uint1 (bool).
- Different TIK APIs provide different data type support.
- TIK is a strongly typed language, which means that calculations between variables of incompatible data types are not allowed.
Standard Data Types of TIK Objects
TIK objects have two standard data types: Scalar and Tensor.
Scalar Definition
TIK provides the Scalar API to define Scalar data. The prototype is as follows:
# API prototype (The name and init_value parameters are optional.) Scalar(dtype="int64", name="reg_buf", init_value=None)
Parameter |
Input/Output |
Description |
|---|---|---|
dtype |
Input |
Data type of the Scalar object, such as: int8, uint8, int16, uint16, float16, int32, uint32, float32, int64, uint64 Defaults to int64. |
name |
Input |
A string specifying the name of the Scalar object. Only digits, letters, and underscores (_) are allowed. Defaults to reg_buf$(COUNT), with COUNT starting at 0. |
init_value |
Input |
Initial value. Supported data types:
NOTICE:
Exprs containing float immediates are not supported. |
# Example 1: Specify all the three parameters. # Define a Scalar data_A of type immediate (float32) whose initial value is 10.2. # Result: data_A: 10.2 data_A = tik_instance.Scalar(dtype = "float32", name = "data_A", init_value = 10.2) # Example 2: Specify all the parameters except name. # Define a Scalar data_B of type immediate (int16) whose initial value is 4. # Result: data_B: 4 data_B = tik_instance.Scalar(dtype = "int16", init_value = 4) # Example 3: Specify all the parameters except name and init_value. # Define a Scalar data_C of type int32. #Result: data_C of type int32 data_C = tik_instance.Scalar(dtype = "int32")
# Note: If init_value is an Expr, the constituent immediate must be of type int, instead of float. # Positive initialization example: Expr (data_a + 1), consisting of a Scalar and an int data_a = tik_instance.Scalar(dtype = "int16", init_value = 1) data_b = tik_instance.Scalar(dtype = "int16", init_value = data_a + 1) # Negative initialization example: Expr (data_a + 1.2), consisting of a Scalar and a float data_a = tik_instance.Scalar(dtype = "int16", init_value = 1) data_b = tik_instance.Scalar(dtype = "int16", init_value = data_a + 1.2)
Tensor Definition
A TIK Tensor defined by the Tensor API call is stored in the storage buffer. Among the parameters, dtype, shape, and scope are your major concerns. The function prototype is as follows.
# API prototype 1 (with only basic parameters specified) data = tik_instance.Tensor(dtype, shape, name = "", scope = tik.xxx) # API prototype 2 (with advanced parameters specified) Tensor(dtype, shape, scope, name, enable_buffer_reuse=False, is_workspace=False, is_atomic_add=False, max_mem_size=None, init_value=None)
Parameter |
Input/Output |
Description |
|---|---|---|
dtype |
Input |
Data type of the Tensor object, such as: uint8, int8, uint16, int16, float16, uint32, int32, float32, uint64, int64 |
shape |
Input |
Shape of the Tensor object. A list or tuple of immediates (of type int or float), Scalars (of type int or uint), or Exprs (of type int or uint). Immediates of the float type are not recommended. Note the following restrictions on the shape:
|
scope |
Input |
Scope of the Tensor object, that is, buffer space where the Tensor object is located:
scope_gm indicates the external storage, and the rest indicate the internal storage. The corresponding calculation can be performed only after the data in the external storage is transferred to the internal storage. |
name |
Input |
A string specifying the name of the Tensor object. Only digits, letters, and underscores (_) are allowed. However, the name must not start with a digit. The tensor name must be unique. NOTE:
When scope is set to scope_gm, the name parameter must not be __fake_tensor. |
enable_buffer_reuse |
Input |
Reserved and not recommended. |
is_workspace |
Input |
A bool. Defaults to False. If it is set to True, the current Tensor is used for storing temporary data only. In this case, scope must be scope_gm and the Tensor must not be included in the input and output tensors. That is, the names of the input and output tensors do not contain the name of the tensor in the workspace. |
is_atomic_add |
Input |
Whether to initialize the Global Memory space. The default value is False.
If the setting is True, scope must be set to scope_gm. NOTE:
This parameter takes effect only when the operator is executed on the network. During network execution, Graph Engine determines whether to initialize the Global Memory space based on this parameter. |
max_mem_size |
Input |
Memory size of the Tensor.
|
init_value |
Input |
A single element or a list/tuple for the Tensor initial value. Note: This parameter is reserved and is not supported in the current version. |
# Define a Tensor data_B of type float16 and size 128, in the Global Memory scope.
data_B = tik_instance.Tensor("float16", (128,), name="data_B", scope=tik.scope_gm)
TIK automatically allocates an address for each Tensor object and avoids address conflicts between data blocks. In addition, TIK automatically checks the data dependency between Tensor objects to implement synchronization.
Note:
The start address of a user-defined Tensor is aligned during memory allocation. General Restrictions describes the alignment requirements of different scopes. If the total size of a buffer type is exceeded due to address alignment, a build error is reported.
Scalar Value Assignment (set_as)
TIK provides the set_as API for setting or changing the value of a Scalar.
from tbe import tik
tik_instance = tik.Tik()
# Example 1: Set the value of Scalar to an immediate (integer).
s1 = tik_instance.Scalar(dtype = "int32") # Declare a Scalar s1 of type int32.
s1.set_as(10) # Set the value of s1 to 10.
# Example 2: Set the value of Scalar to an immediate (float).
s2 = tik_instance.Scalar(dtype = "float16") # Declare a Scalar s2 of type float16.
s2.set_as(10.2) # Set the value of s2 to 10.2.
# Example 3: Set the value of Scalar to that of another Scalar.
s3 = tik_instance.Scalar(dtype = "float16", init_value = 2.4) # Declare a Scalar s3 whose initial value is 2.4.
s4 = tik_instance.Scalar(dtype = "float16") # Declare a Scalar s4 of type float16.
s4.set_as(s3) # Set the value of s4 to that of s3.
# Example 4: Set the value of a Scalar to that of an Expr (consisting of a Scalar and an immediate).
s5 = tik_instance.Scalar(dtype = "int32", init_value = 1) # Declare a Scalar s5 whose initial value is 1.
s6 = tik_instance.Scalar(dtype = "int32") # Declare a Scalar s6 of type int32.
s6.set_as(s5+20) # Set the value of s6 to that of Expr(s5+20), resulting in: s6 = 21.
# Example 5: Set the value of a Scalar to that of a Tensor.
s7 = tik_instance.Scalar(dtype = "int16")
tensor_a = tik_instance.Tensor("int16", (4,), name="tensor_a", scope=tik.scope_ubuf)
s7.set_as(tensor_a[0])
'''
For example:
tensor_a = [1,2,3,4]
Value assignment result:
s7 = 1
'''
Tensor Value Assignment
TIK provides two methods for setting or changing the value of a Tensor.
- Method 1: Call set_as to set the value of a Tensor.
# Example: from tbe import tik tik_instance = tik.Tik() data_A = tik_instance.Tensor("int16", (4,), name="data_A", scope=tik.scope_ubuf) data_B = tik_instance.Tensor("int16", (4,), name="data_B", scope=tik.scope_ubuf) data_B[1].set_as(4) # Set the value of the second element of data_B to 4 (1) Data_A[0].set_as(data_B[1]) # Set the value of the first element of data_A to the second element of data_B. (2) ''' For example: data_A = [1, 1, 1, 1] data_B = [0, 0, 0, 0] Result of statement (1): data_B = [0, 4, 0, 0] Result of statement (2): data_A = [4, 1, 1, 1] ''' - Method 2: Call Changing the Tensor Content to set the value of a specific element of a Tensor array by index.
# Example: ''' The initial shape of data_A is [1.2, 2.4, 3.6, 4.8]. ''' from tbe import tik tik_instance = tik.Tik() data_A = tik_instance.Tensor("float16", (4,), name="data_A", scope=tik.scope_ubuf) scalar_B = tik_instance.Scalar(dtype="float16", name="scalar_B", init_value=2.0) scalar_idx = tik_instance.Scalar(dtype="int16", name="scalar_idx", init_value=1) # Assuming index = 0 and value = 1.0, the following statement sets the value of the first element of data_A to an immediate. data_A[0] = 1.0 ''' Result: data_A = [1.0, 2.4, 3.6, 4.8] ''' # Assuming index = scalar_idx and value = scalar_B, the following statement sets the value of the second element of data_A to that of a Scalar. data_A[scalar_idx] = scalar_B ''' Result: data_A = [1.0, 2.0, 3.6, 4.8] ''' # Assuming index is an Expr consisting of a Scalar (int16) and an immediate (int) and value is an Expr consisting of a Scalar (float16) and an immediate (float), the following statement sets the value of the third element of data_A to an Expr. data_A[scalar_idx+1] = scalar_B + 2.2 ''' Result: data_A = [1.0, 2.0, 4.2, 4.8] '''
In addition to the preceding two methods for setting the value of a Tensor, you can also use the Tensor compute APIs. For example, the vec_add API computes addition and assigns the addition result to the destination Tensor.
Tensor Shape Obtaining
TIK provides the shape API for obtaining the shape of a Tensor.
# Example:
from tbe import tik
tik_instance = tik.Tik()
data_A = tik_instance.Tensor("float16", (128,), name="data_A", scope=tik.scope_ubuf)
data_A.shape # Output the shape of data_A: [128].
Tensor Reshaping
TIK provides the reshape API for reshaping a Tensor.
# Example:
from tbe import tik
tik_instance = tik.Tik()
data_A = tik_instance.Tensor("int16", (4,), name="data_A", scope=tik.scope_gm)
# Reshape data_A from 4 x 1 to 2 x 2.
data_A.reshape((2,2))
'''
The initial shape of data_A is [1, 2, 3, 4].
Reshape result:
data_A = [[1, 2], [3, 4]]
'''
Partial Tensor Data Obtaining by Index Slice
The Obtaining Partial Tensor Data TIK API obtains the partial data in a Tensor based on the element index of the Tensor array and returns a new Tensor.
# Example:
from tbe import tik
tik_instance = tik.Tik()
data_A = tik_instance.Tensor("int16", (8,), name="data_A", scope=tik.scope_gm)
data_B = tik_instance.Tensor("int16", (4,), name="data_B", scope=tik.scope_gm)
data_B = data_A[4:] # Slice data_A and set data_B to the fifth elements and all subsequent elements of data_A.
'''
For example:
data_A = [1,2,3,4,5,6,7,8]
Result:
data_B = [5,6,7,8]
'''
Data Re-interpreting in New Data Type
The reinterpret_cast_to TIK API re-interprets specified bytes in the memory in a new data type.
Assuming that there are 128 elements of type float16, the reinterpret_cast_to call can read the data in float32, resulting in 64 elements of type float32. Therefore, reinterpret_cast_to does not actually convert the precision, which is a function implemented by the vec_conv API instead.
# Example:
from tbe import tik
tik_instance = tik.Tik()
data_A = tik_instance.Tensor("float16", (16,), name="data_A", scope=tik.scope_gm)
data_B = data_A.reinterpret_cast_to("uint32")
data_C = data_B.reinterpret_cast_to("float16")
""" Result:
Input: # data_A is of type float16.
data_A:
[ 4.812e+00 1.870e-04 -5.692e-02 2.528e-02 -9.225e+02 -1.431e+02
-1.541e+01 -2.018e-03 1.653e-03 -4.090e+00 2.016e+01 -5.846e+04
-8.072e-03 2.627e+00 -3.174e-02 -3.088e-01]
Returns:
data_B: # data_B is read in the uint32 format.
[ 169952464 645507913 3631866677 2552417204 3289847493 4213394698
1094819874 3035736080]
data_C: # data_C is read in the float16 format.
[ 4.812e+00 1.870e-04 -5.692e-02 2.528e-02 -9.225e+02 -1.431e+02
-1.541e+01 -2.018e-03 1.653e-03 -4.090e+00 2.016e+01 -5.846e+04
-8.072e-03 2.627e+00 -3.174e-02 -3.088e-01]
"""