1 # SVP64 REMAP Worked Example: Matrix Multiply
5 * [Online matrix calculator](https://matrix.reshish.com/multCalculation.php)
7 * <https://bugs.libre-soc.org/show_bug.cgi?id=701>
9 TODO: Include screenshots
11 One of the most powerful and versatile modes of the REMAP engine (a part of
12 the SVP64 feature set) is the ability to perform matrix
13 multiplication with all elements within a scalar register file.
15 This is done by converting the index used to iterate over the operand and
16 result matrices to the actual index inside the scalar register file.
18 ## Worked example - manual (conventional method)
20 The matrix multiply looks like this:
24 When multiplying non-square matrices (rows != columns), to determine the
25 dimension of the result when matrix X has `a` rows and `b` columns and matrix
26 Y has `b` rows and `c` columns:
30 The result matrix will have number of rows of the first matrix, and number of
31 columns of the second matrix.
34 For this example, the following values will be used for the operand matrices X
35 and Y, result Z shown for completeness.
37 X =| 1 2 3 | Y = | 6 7 | Z = | 52 58 |
38 | 3 4 5 | | 8 9 | | 100 112 |
41 Matrix X has 2 rows, 3 columns (2x3), and matrix Y has 3 rows, 2 columns.
43 To determine the final dimensions of the resultant matrix Z, take the number
44 of rows from matrix X (2) and number of columns from matrix Y (2).
46 The method usually taught in linear algebra course to students is the
47 following (outer product):
49 1. Start with the first row of the first matrix, and first column of the
51 2. Multiply each element in the row by each element in the column, and sum
52 with the current value of the result matrix element (multiply-add-accumulate).
53 Store result in the first row, first column entry.
54 3. Move to the next column of the second matrix, and next column of the result
55 matrix. If there are no more columns in the second matrix, go back to first
56 column (second matrix), and move to next row (first and result matrices).
57 If there are no more rows left, result matrix has been fully computed.
59 4. Move to the next row of the first matrix, and next column of the second matrix.
61 This for-loop uses the indices as shown above
64 for i in range(mat_X_num_rows):
65 for k in range(0, mat_Y_num_cols):
66 for j in range(0, mat_X_num_cols): # or mat_Y_num_rows
67 mat_Z[i][k] += mat_X[i][j] * mat_Y[j][k]
73 | 1 2 3 | | 6 7 | = | (1*6 + 2*8 + 3*10) (1*7 + 2*9 3*11) |
74 | 3 4 5 | * | 8 9 | | (3*6 + 4*8 + 5*10) (3*7 + 4*9 5*11) |
77 | 1 2 3 | | 6 7 | = | ( 6 + 16 + 30) ( 7 + 18 + 33) |
78 | 3 4 5 | * | 8 9 | | (18 + 32 + 50) (21 + 36 + 55) |
81 | 1 2 3 | | 6 7 | = | 52 58 |
82 | 3 4 5 | * | 8 9 | | 100 112 |
86 For the algorithm, assign indeces to matrices as follows:
93 Mat Y | 6 7 8 9 10 11 |
96 Mat Z | 52 58 100 112 |
99 (Start with the first row, then assign index left-to-right, top-to-bottom.)
104 Mat X | Mat Y | Mat Z
120 The issue with this algorithm is that the result matrix element is the same
121 for three consecutive operations, and where each element is stored in CPU
122 registers, the same register will be written to three times and thus causing
127 A slight modification to the order of the loops in the algorithm massively
128 reduces the chance of read-after-write hazards, as the result matrix
129 element (and thus register) changes with every multiply-add operation.
134 for i in range(mat_X_num_rows):
135 for j in range(0, mat_X_num_cols): # or mat_Y_num_rows
136 for k in range(0, mat_Y_num_cols):
137 mat_Z[i][k] += mat_X[i][j] * mat_Y[j][k]
143 Mat X | Mat Y | Mat Z
158 The index for the result matrix changes with every operation, and thus the
159 consecutive multiply-add instruction doesn't depend on the previous write
162 # SVP64 instructions implementing matrix multiply
164 * SVP64 assembler example:
165 [unit test](https://git.libre-soc.org/?p=openpower-isa.git;a=blob;f=src/openpower/decoder/isa/test_caller_svp64_matrix.py;hb=30f2d8a8e92ad2939775f19e6a0f387499e9842b#l56)
166 * SVREMAP and SVSHAPE instructions defined in:
168 * Multiple-Add Low Doubleword instruction pseudo-code (OpenPOWER ISA 3.0C
169 Book I, section 3.3.9): [[openpower/isa/fixedarith]]
171 *(Need to check if first arg of svremap correct, then one shown works with
174 svshape 2, 2, 3, 0, 0
175 svremap 31, 1, 2, 3, 0, 0, 0
176 sv.maddld *0, *16, *32, *0
180 The `svshape` instruction is a convenient way to access the SHAPE Special
181 Purpose Registers (SPRs), which were added alongside the SVP64 looping
182 system for complex element indexing. Without having SHAPE SPRs, only the most
183 basic, consecuting indexing of register elements (0,1,2,3...) would
186 ### SHAPE Remapping SPRs
188 * See [[sv/remap]] for the full break down of SPRs SHAPE0-3.
190 For Matrix Multiply, SHAPE0 SPR is used:
192 |0:5 |6:11 | 12:17 | 18:20 | 21:23 |24:27 |28:29 |30:31|
193 |----- |----- | ------- | ------- | ------ |------|------ |---- |
194 |xdimsz|ydimsz| zdimsz | permute | invxyz |offset|skip |mode |
199 - 0b00 indicates no dimensions to be skipped
200 - 0b01 - skip '1st dim'
201 - 0b10 - skip '2nd dim'
202 - 0b11 - skip '3rd dim'
204 invxyz (3-bit; 1 for x, 1 for y, 1 for z):
206 - If corresponding dim bit is zero, start index from zero and increment
207 - If bit set, start from xdimsz-1 (x dimension size, or whichever dimension
208 bit is being looked at) and decrement down to zero.
210 offset is used to offset the result by `offset` elements (important for when
211 using element width overrides are used).
213 xdimsz, ydimsz, zdimsz are offset by 1, such that 0-0b111111 correspond to
214 1-64. A value of xdimsz=2 would indicate that in the first dimension there are
215 3 elements in the array.
217 With the example Matrix X (2 rows, 3 columns, or 2x3 matrix), xdimsz=1,
221 | permute | order | array format |
222 | ------- | ----- | ------------------------ |
223 | 000 | 0,1,2 | (xdim+1)(ydim+1)(zdim+1) |
224 | 001 | 0,2,1 | (xdim+1)(zdim+1)(ydim+1) |
225 | 010 | 1,0,2 | (ydim+1)(xdim+1)(zdim+1) |
226 | 011 | 1,2,0 | (ydim+1)(zdim+1)(xdim+1) |
227 | 100 | 2,0,1 | (zdim+1)(xdim+1)(ydim+1) |
228 | 101 | 2,1,0 | (zdim+1)(ydim+1)(xdim+1) |
229 | 110 | 0,1 | Indexed (xdim+1)(ydim+1) |
230 | 111 | 1,0 | Indexed (ydim+1)(xdim+1) |
232 Permute re-arranges the order of the nested for-loops used to iterate over the
233 three dimensions. This allows for in-place transpose, in-place rotate, matrix
234 multiply, convolutions, without the limitation of Power-of-Two matrices.
236 Limitations of Matrix REMAP are currently:
238 - Vector Length (VL) limited to 127, and up to 127 Multiply-Add Accumulates
239 (MAC), or other operations may be performed in total.
240 For matrix multiply, it means both operand matrices and result matrix can
241 have no more than 127 elements in total.
242 (Larger matrices can be split into tiles to circumvent this issue, out
243 of scope of this document).
244 - `svshape` instruction only provides part of the Matrix REMAP capability.
245 For rotation and mirroring, SVSHAPE SPRs must be programmed directly (thus
246 requiring more assembler instructions). Future revisions of SVP64 will
247 provide more comprehensive capacity, mitigating the need to write to SVSHAPE
250 Going back to the assembler instruction used to setup the shape for matrix
254 svshape 2, 2, 3, 0, 0
259 - SVxd=2, SVyd=2, SVzd=3
260 - SVRM=0 (Matrix mode, uses SHAPE0 SPR)