The function execution space qualifier indicates whether the function is executed on the host or device, and the space scope that can be called.
Function Execution Space Qualifier |
Execution Space |
Function Space Call Allowed |
||
|---|---|---|---|---|
Host |
Device |
Host |
Device |
|
__host__, no qualifier |
√ |
x |
√ |
x |
__aicore__ |
x |
√ |
x |
√ |
__global__ |
x |
√ |
√ |
x |
A function modified by __global__ is the entry of a kernel function and has the following restrictions:
Functions decorated with __aicore__ can be executed only on the device and can be called only by __global__ functions or other __aicore__ functions.
Functions decorated with __host__ can be called and executed only on the host.
The memory space qualifier __ubuf__ is used to indicate dynamic and static memory. The size of the static memory is determined during compilation, and the size of the dynamic memory is determined during kernel function execution.
The current version does not support dynamic and static memory.
1 | __ubuf__ half staticBuf[1024]; |
1 | extern __ubuf__ half dynamicBuf[]; |
The actual size of the dynamic memory needs to be configured when the kernel function is started. For details, see Kernel Function Configuration.
Specifies and obtains the built-in structure of the thread grid and thread block in the x, y, and z dimensions.
1 2 3 | dim3(x); // Create a one-dimensional structure. The default value 1 is used for dimy and dimz. dim3(x, y); // Create a two-dimensional structure. The default value 1 is used for dimz. dim3(x, y, z); // Create a three-dimensional structure. |
Currently, the following built-in variables of the dim3 structure are provided, which are available only on the device:
Built-in global variable, which can be used only in kernel functions. It indicates the number of thread blocks in each dimension of the entire compute task.
Built-in global variable, which can be directly used in the kernel function to obtain the three-dimensional hierarchy of threads configured in the thread block, that is, the value of the dim3 structure instance configured when the VF is started. blockDim.x, blockDim.y, and blockDim.z indicate the numbers of threads in the three dimensions of a thread block, respectively.
Built-in global variable, which can be used only in the kernel function to obtain the block index. It indicates the coordinates of the thread block where the current thread is located in the entire grid.
Built-in global variable, which can be directly used in the kernel function to obtain the index of the current thread in the thread block. threadIdx.x, threadIdx.y, and threadIdx.z indicate the indexes of the current thread in three dimensions, respectively. The value range is [0, blockDim.x) for threadIdx.x, [0, blockDim.y) for threadIdx.y, and [0, blockDim.z) for threadIdx.z. The mapping between the thread index and thread ID in a thread block is as follows:
Currently, the following built-in variables of the int type are provided, which are available only on the device:
Type |
Data Type |
Description |
Size (Bit) |
Value Range |
Boolean |
bool |
Bool type, which occupies 8 bits. When all bits are 0, it represents false; any non-zero value represents true. |
8 |
true, flase |
Integer |
uint8_t |
unsigned char |
8 |
[0, 255] |
int8_t |
signed char |
8 |
[-128, 127] |
|
uint16_t |
unsigned short |
16 |
[0, 65535] |
|
int16_t |
signed short |
16 |
[-32768, 32767] |
|
uint32_t |
unsigned int |
32 |
[0, 4294967295] |
|
int32_t |
signed int |
32 |
[-2147483648, 2147483647] |
|
uint64_t |
unsigned long |
64 |
[0, 18446744073709551615] |
|
int64_t |
signed long |
64 |
[-9223372036854775808, 9223372036854775807] |
|
Float |
float8_e4m3_t |
1-bit sign width, 4-bit exponent width, and 3-bit mantissa width |
8 |
[26 - 29, 29 - 26] |
float8_e5m2_t |
1-bit sign width, 5-bit exponent width, and 2-bit mantissa width |
8 |
[213 - 216, 216 - 213] |
|
hifloat8_t |
1-bit sign width, 2-bit point field width, and exponent and mantissa widths determined by the point field code |
8 |
The point field code determines the data precision and value range. |
|
half |
1-bit sign width, 5-bit exponent width, and 10-bit mantissa width |
16 |
[25 - 216, 216 - 25] |
|
bfloat16_t |
1-bit sign width, 8-bit exponent width, and 7-bit mantissa width |
16 |
[2120 - 2128, 2128 - 2120] |
|
float |
1-bit sign width, 8-bit exponent width, and 23-bit mantissa width |
32 |
[2104 - 2128, 2128 - 2104] |
Element Data Type |
Vector X2 |
Vector X4 |
unsigned char |
uchar2 |
uchar4 |
signed char |
char2 |
char4 |
unsigned short (16bit) |
ushort2 |
ushort4 |
signed short (16bit) |
short2 |
short4 |
unsigned int |
uint2 |
uint4 |
signed int |
int2 |
int4 |
Unsigned long integer (64 bit) |
ulonglong2 |
ulonglong4 |
Signed long integer (64 bit) |
longlong2 |
longlong4 |
Unsigned long integer (32 bit) |
ulong2 |
ulong4 |
Signed long integer (32 bit) |
long2 |
long4 |
Floating-point type, 1 sign bit, 2 exponent bits, and 1 mantissa bit |
float4_e2m1x2_t |
- |
Floating-point type, 1 sign bit, 1 exponent bit, and 2 mantissa bits |
float4_e1m2x2_t |
- |
Floating-point type, 1 sign bit, 4 exponent bits, and 3 mantissa bits |
float8_e4m3x2_t |
- |
Floating-point type, 1 sign bit, 5 exponent bits, and 2 mantissa bits |
float8_e5m2x2_t |
- |
Floating-point type (hif8) |
hifloat8x2_t |
- |
Floating-point type, 1 sign bit, 5 exponent bits, and 10 mantissa bits |
half2 |
- |
Floating-point type, 1 sign bit, 8 exponent bits, and 7 mantissa bits |
bfloat16x2_t |
- |
Floating-point type, 1 sign bit, 8 exponent bits, and 23 mantissa bits |
float2 |
float4 |
Data Type |
Memory Size (Bytes) |
Address Alignment (Bytes) |
char2, uchar2 |
2 |
2 |
char4, uchar4 |
4 |
4 |
short2, ushort2 |
4 |
4 |
short4, ushort4 |
8 |
8 |
int2, uint2 |
8 |
8 |
int4, uint4 |
16 |
16 |
long2, ulong2 |
8 |
8 |
long4, ulong4 |
16 |
16 |
longlong2, ulonglong2 |
16 |
16 |
longlong4, ulonglong4 |
32 |
32 |
float2 |
8 |
8 |
float4 |
16 |
16 |
float4_e2m1x2_t, float4_e1m2x2_t |
1 |
1 |
float8_e4m3x2_t, float8_e5m2x2_t, hifloat8x2_t |
2 |
2 |
half2, bfloat16x2_t |
4 |
4 |
Type |
Operator |
bool |
int8_t/uint8_t/int16_t/uint16_t/int32_t/uint32_t/int64_t/uint64_t |
half/bfloat16_t/float |
half2/bfloat16x2_t |
hifloat8_t |
|---|---|---|---|---|---|---|
Arithmetic operator |
+ |
x |
√ |
√ |
√ |
x |
- |
x |
√ |
√ |
√ |
x |
|
* |
x |
√ |
√ |
√ |
x |
|
/ |
x |
√ |
√ |
√ |
x |
|
% |
x |
√ |
x |
x |
x |
|
++ |
x |
√ |
√ |
√ |
x |
|
-- |
x |
√ |
√ |
√ |
x |
|
- (inverted) |
x |
√ |
√ |
√ |
x |
|
Comparison operator |
< |
x |
√ |
√ |
x |
x |
<= |
x |
√ |
√ |
x |
x |
|
> |
x |
√ |
√ |
x |
x |
|
>= |
x |
√ |
√ |
x |
x |
|
== |
x |
√ |
√ |
x |
x |
|
!= |
x |
√ |
√ |
x |
x |
|
Bitwise operator |
& |
x |
√ |
x |
x |
x |
| |
x |
√ |
x |
x |
x |
|
^ |
x |
√ |
x |
x |
x |
|
~ |
x |
√ |
x |
x |
x |
|
<< |
x |
√ |
x |
x |
x |
|
>> |
x |
√ |
x |
x |
x |
|
Logical operator |
&& |
√ |
√ |
√ |
x |
x |
|| |
√ |
√ |
√ |
x |
x |
|
! |
√ |
√ |
√ |
x |
x |
|
Condition operator |
a ? b : c |
√ |
√ |
√ |
√ |
x |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // Addition operation res[idx] = x[idx] + y[idx]; // Inversion operation x[idx] = (-x[idx]); // Comparison operation if (x[idx] > y[idx]) { res[idx] = x[idx]; } else { res[idx] = y[idx]; } // Bitwise AND operation res[idx] = x[idx] & y[idx]; // Logical OR operation if (x[idx] || y[idx]) { res[idx] = 1; } // Condition operation res[idx] = x[idx] > y[idx] ? x[idx] : y[idx]; |
The execution configuration must be specified when a function modified by the __global__ qualifier is called. The execution configuration is inserted between the parameter list enclosed in parentheses following the function name, for example:
1 | <<<grid_dim, block_dim, dynamic_mem_size, stream>>> |
Where:
1 2 3 4 | // Declaration __global__ void add_custom(float* x, float* y, float* z, uint64_t total_length); // Call add_custom<<<block_num, thread_num_per_block, dyn_ubuf_size, stream>>>(x, y, z, 1024); |
Before executing the function, the preceding configuration parameters are validated. If grid_dim or block_dim exceeds the maximum size allowed by the device, or dynamic_smem_bytes exceeds the remaining available shared memory after static memory allocation, the function will fail to execute.
When multiple threads are executed concurrently, using fewer registers per thread allows more threads and thread blocks to reside in the Ascend AI Processor, thereby improving performance. Therefore, the compiler uses heuristic algorithms to minimize register spilling and the number of instructions, while also reducing the number of used registers. Applications can use the __launch_bounds__() qualifier in the __global__ function definition to restrict the launch bounds, providing additional information to assist the compiler in optimizing this process. This is an optional configuration.
The maximum number of threads determines the number of registers that can be allocated to each thread. The following table lists the mapping between the maximum number of threads and the number of available registers for each thread. Registers are used to store local variables in threads. If the number of local variables exceeds the number of registers, problems such as stack overflow may occur. It is recommended that the maximum number of threads be the same as the number of dim3 threads for starting the VF task.
Number of Threads |
Number of Available Registers for Each Thread |
|---|---|
1025–2048 |
16 |
513–1024 |
32 |
257–512 |
64 |
1–256 |
127 |
The following is an example of setting the maximum number of threads for the SIMT function to 512:
1 | __simt_vf__ __launch_bounds__(512) inline void add(__gm__ uint8_t* x, __gm__ uint8_t* y, __gm__ uint8_t* z) |