Note
Go to the end to download the full example code.
Vector Addition
This tutorial demonstrates vector addition operator and its launch on Ascend simulator.
16 import asc2
17 import torch
18
19
20 # The functions which are executed on Ascend NPU must be marked with `@asc2.jit` decorator.
21 # Available parameters for @asc2.jit decorator can be seen in the documentation:
22 # https://compiler-team-ru.github.io/pyasc/python-api/rst/runtime/index.html
23 @asc2.jit
24 def vector_add(
25 # Pointers to input and output tensors should have `asc2.GlobalAddress` type.
26 x_ptr: asc2.GlobalAddress, y_ptr: asc2.GlobalAddress, out_ptr: asc2.GlobalAddress,
27 # Scalar parameters are passed as Python types (e.g. `int`, `float`).
28 # For optimization purposes it is recommended to pass scalar parameter as constants (e.g. `asc2.ConstExpr[int]`).
29 size: int,
30 # tile_size is used in asc2.copy_in must be asc2.ConstExpr
31 tile_size: asc2.ConstExpr):
32
33 # Tensor descriptor is created from `asc2.GlobalAddress` to represent entire tensor.
34 x_gm = asc2.global_tensor(x_ptr, [size])
35 y_gm = asc2.global_tensor(y_ptr, [size])
36 out_gm = asc2.global_tensor(out_ptr, [size])
37
38 # Python expressions are used to calculate offset and define the loop iterating over tiles:
39 # `asc2.block_num()` function provides number of AICOREs launched.
40 block_loop_num = asc2.ceildiv(asc2.ceildiv(size, asc2.block_num()), tile_size)
41 # `asc2.block_idx()` function is used to get current AICORE index.
42 block_offset = asc2.block_idx() * tile_size * block_loop_num
43
44 # `unroll_factor` parameter of `asc2.range` in `for` loop can be used to manage software pipelining. Set it to `2` to enable double buffering.
45 # `parallel` parameter of `asc2.range` in `for` loop enable overlapping of store operation of `i`-th iteration and load of `i+1`-th iteration.
46 # It is user responsibility to ensure that there are no data dependencies between overlapped iterations.
47 for i in asc2.range(block_loop_num, unroll_factor=2, parallel=True):
48 tile_offset = block_offset + i * tile_size
49 # `asc2.copy_in` is used to create a local tensor object which is used for further calculations. Data movement from GM to UB happens in this operation.
50 x = asc2.copy_in(x_gm, [tile_offset], [tile_size])
51 y = asc2.copy_in(y_gm, [tile_offset], [tile_size])
52 # One or more operations can be applied for tensors. It is compiler responsibility to allocate required number of memory blocks in UB.
53 out = x + y
54 # `asc2.copy_out` is used to move data from UB back to GM.
55 asc2.copy_out(out, out_gm, [tile_offset])
56
57
58 if __name__ == "__main__":
59 backend = asc2.Backend.Model # can be "Model" for simulator or "NPU" for device
60 soc_version = asc2.Platform.Ascend950PR_9599 # Device version
61 device_id = 0 # might be necessary to provide if more than one NPU device is present in the system
62 asc2.set_platform(backend, soc_version, device_id)
63
64 size = 8192
65 block_num = 16
66 tile_size = 128
67 dtype = torch.float32
68 # Allocate tensors
69 x = torch.randn(size, dtype=dtype)
70 y = torch.randn(size, dtype=dtype)
71 out = torch.empty_like(x)
72
73 # For the kernel invocation, number of AICOREs should be provided in brackets:
74 vector_add[block_num](x, y, out, size, tile_size)
75 torch.testing.assert_close(out, x + y)