Learning Objectives
After completing this tutorial, you should know how to:- Define memory DSDs for tensor accesses
- Use memory DSDs in builtin operations on tensors
- Use builtins to initialize tensors
Example Overview
Your program will run on a single processing element (PE). Like the previous tutorial, this tutorial demonstrates the program with a simulated fabric consisting of an 8 x 3 block of PEs. The problem steps are identical to the previous tutorial. The layout file, host code, and compile and run commands are also identical. Onlype_program.csl needs to change, and this tutorial takes
a closer look at those changes.
Modify the CSL
The previous tutorial created a complete CSL program using a single PE to initialize and computey = Ax + b.
What needs to change in pe_program.csl to take advantage of
memory DSDs and builtin operations on tensors?
- Define DSDs for accessing the tensors
- Rewrite the
gemvfunction to operate on these DSDs
layout.csl, which is the same for
this tutorial.
The new pe_program.csl is included below, with the
changes highlighted.
Define the Memory DSDs
First, take a look at the DSDs defined for accessingb and y:
b_dsd and y_dsd are the memory DSDs for
accessing b, and y, respectively.
The tensor_access field defines the access pattern of these DSDs.
|i| specifies the induction variable, and {M} specifies
the loop bound; i.e., these DSDs access M elements.
After ->, an expression is given for accessing a memory location
using the induction variable.
This expression must be affine, or linear plus a constant.
The access pattern for these DSDs is straightforward: these DSDs
loop over all M elements, in order, of their respective tensors.
Now take a look at the DSD for accessing A:
M elements of A, but strided by N elements;
i.e., A_dsd accesses elements 0, N, 2*N, ... (M-1)*N.
Because A is stored in row major format, this means that A_dsd
as defined here accesses the 0th column of A.
These memory DSDs are of type
mem1d_dsd, which are one-dimensional
memory DSDs. CSL also provides mem4d_dsd, multidimensional memory
DSDs for up to four dimensions.You can learn more about memory DSDs in the language reference guide
Data Structure Descriptors.Use the DSDs to Compute GEMV
Now that the DSDs are defined, take a look at how to use them to compute GEMV. Recall that the previousgemv() function was defined as follows:
gemv() looks like this:
N,
instead of two explicit loops.
At each iteration, this @fmacs operation does the following:
- performs a vector-scalar multiplication between the column of
Areferenced byA_dsdand the scalarx[i], - performs an elementwise vector addition between this result and
the vector
y, - and stores this final result into
y.
@fmacs operation increments the M elements of y
by the vector-scalar product of column i of A
and element i of x.
The @increment_dsd_offset operation at each loop iteration increments
A_dsd to reference the next column of A.
This builtin operation takes A_dsd and creates a new DSD by offsetting
its access by 1 f32 element.
For instance, the first time this operation occurs, A_dsd will now
access elements 1, N+1, 2*N+1, ... (M-1)*N+1 of A.
Again, because A is stored row major,
this will access the 1st column of A.
Once this loop over the N columns of A is complete,
y contains the result of A*x.
The @fadds operation performs an elementwise vector addition between
y and b, storing the result back in y.
Now y contains the result of A*x + b.
Use Builtins to Initialize Tensors
You may have noticed one other slight change to this code. Instead of initializingx, b, and y, in the initialize function,
this code uses builtins to provide values for them at declaration:
@constants builtin returns a tensor of the specified type,
with all elements initialized to the specified value.
Thus, x is initialized as an N element tensor of all ones,
and b is initialized as an M element tensor of all twos.
The @zeros builtin is rather obvious. y is initialized as an M
element tensor of all zeros.
Compile and Run the Program
As with the previous tutorial, compile and run this code using:SUCCESS! message at the end of execution.
Exercises
A is stored row-major in the above code.
How would you rewrite A_dsd and the gemv function
if A were stored column major instead?
Next
In the next tutorial, you’ll use host-to-devicememcpy,
and copy host-initialized values for A, x, and b onto
the device.