GeezerVison is showing docs.nvidia.com — Matrix Multiplication — CUDA Tile C++ API Reference 13.5 documentation
Matrix Multiplication
Matrix multiplication of tiles is supported through the ct::matmul(), ct::mma(), and
ct::scaled_mma() functions. The ct::mma and ct::scaled_mma functions accept an
accumulator argument that is added to the multiplication result. These functions can perform
batched matrix multiplies when the arguments have rank \(3\).
The result of the matrix multiplication operations is an approximation of an infinitely precise
mathematical computation. The exhaustive numerical behavior for these operation is not currently
specified. Information about the error bounds, rounding modes, and the behavior of non-finite,
signed zero, and subnormal arguments may be absent.
Additionally, there are unspecified scenarios where overflow may occur while performing matrix
multiplication of integral operands. In these scenarios, the result of the operation is
unspecified.
cuda::tiles::mma_compatible
-
template<typename L, typename R, typename A>
concept mma_compatible = ct::numeric_tile<L> && ct::numeric_tile<R> && ct::numeric_tile<A> && /* atomic constraint */;
-
Indicates whether the types \(L\) (the left matrix), \(R\) (the right matrix)
and \(A\) (the accumulator matrix) can participate in a matrix
multiply accumulate operation.
The types \(L\), \(R\) and \(A\) can participate in a matrix multiply accumulate if
The element types of \(L\) and \(R\) are integral scalars
of the same bitwidth or they are floating point scalars of the same conversion rank
-
The element types of \(L\), \(R\) and \(A\) are
selected from a row in the following table:
Element Type of \(L\) and \(R\) |
Element type of \(A\) |
\(8\) bit integral types |
\(32\) bit signed integral types |
|
|
|
|
|
|
|
|
|
All of \(L\), \(R\) and \(A\) have the same rank and the rank is either \(2\)
or \(3\).
-
In the case where the rank is \(2\), the shapes of
\(L\), \(R\) and \(A\) are taken from the table for some
integers \(N\), \(K\) and \(M\):
Shape of \(L\) |
Shape of \(R\) |
Shape of \(A\) |
\(N \times K\) |
\(K \times M\) |
\(N \times M\) |
-
In the case where the rank is \(3\), the shapes of
\(L\), \(R\) and \(A\) are taken from the table for some
integers \(A\), \(B\), \(C\), \(N\), \(K\) and \(M\):
Shape of \(L\) |
Shape of \(R\) |
Shape of \(A\) |
\(A \times N \times K\) |
\(B \times K \times M\) |
\(C \times N \times M\) |
Additionally \(A\) and \(B\) satisfy the following predicates:
\[\begin{split}\begin{cases}
A = C & \text{or} \\
A = 1 &
\end{cases} \quad \quad \begin{cases}
B = C & \text{or} \\
B = 1 &
\end{cases}\end{split}\]
cuda::tiles::matmul_compatible
-
template<typename L, typename R>
concept matmul_compatible = ct::numeric_tile<Lhs> && ct::numeric_tile<Rhs> && /* atomic constraint */;
-
Indicates whether the types \(L\) and \(R\) may participate in a matrix multiply
operation.
The types \(L\) and \(R\) may participate in a matrix multiply operation if:
The element types of \(L\) and \(R\) are integral scalars whose
bitwidth is \(8\) or they are floating point scalars of the same conversion
rank.
\(L\) and \(R\) have the same rank and that rank is either
\(2\) or \(3\).
-
In the case where the rank is \(2\), the shapes of \(L\) and
\(R\) are taken from the table below for some integers \(N\), \(K\) and \(M\):
Shape of \(L\) |
Shape of \(R\) |
\(N \times K\) |
\(K \times M\) |
The hypothetical ct::extents specialization of dimensions \(N \times M\) must be
tile compatible.
-
In the case where the rank is \(3\), the shapes of
\(L\) and \(R\) are taken from the table below for some
integers \(A\), \(B\), \(N\), \(K\) and \(M\):
Shape of \(L\) |
Shape of \(R\) |
\(A \times N \times K\) |
\(B \times K \times M\) |
Additionally, either \(A = B\) or at least one
of them is equal to \(1\). The hypothetical ct::extents specialization
whose dimensions are \(\operatorname{max}(A, B) \times N \times M\) must be
tile compatible.
cuda::tiles::matmul_result_t
-
template<ct::numeric_tile L, ct::numeric_tile R>
requires ct::matmul_compatible<L, R> using matmul_result_t = /* see below */;
-
Yields the result type of performing matrix multiplication on operands of
types \(L\) and \(R\)
The result type is a specialization of ct::tile. The element type
of the result is determined by the element types of \(L\) and \(R\)
according to the following table:
\(L\) and \(R\) element type |
Result Element Type |
8 bit integral types |
int32_t
|
|
|
__half |
|
|
float |
|
|
double |
The shape of the result depends on the rank of \(L\) and \(R\):
If \(L\) and \(R\) have rank is \(2\) with shapes
\(N \times K\) and \(K \times M\) respectively,
the result shape is \(N \times M\).
If \(L\) and \(R\) have rank \(3\) with shapes \(A \times N \times K\) and
\(B \times K \times M\), the result shape is \(C \times N \times M\) where \(C\) is the larger of
\(A\) and \(B\).
Note
The result element type for matmul cannot be configured. To use a
different element type, use ct::mma() with a zeroed accumulator.
cuda::tiles::mma
-
template<ct::numeric_tile L, ct::numeric_tile R, ct::numeric_tile A>
requires ct::mma_compatible<L, R, A> __tile__ A mma(L lhs, R rhs, A acc) noexcept;
-
Performs matrix multiply on lhs and rhs and adds the result to acc. For
rank \(3\) arguments, the matrix multiply accumulate is performed
for each set of matrices along the first dimension of the operands.
If the operands have rank \(3\), the lhs undergoes
broadcast conversion to the shape \(A_0 \times L_1 \times L_2\)
and rhs undergoes broadcast conversion to the
shape \(A_0 \times R_1 \times R_2\).
Let \(a\), \(b\) and \(c\) denote the converted operands lhs,
rhs, and acc respectively.
For rank \(2\) arguments, the result is an approximation of the matrix \(r\) determined by
\[r(i_0, i_1) = \sum_{k = 0}^{k < L_1} a(i_0, k) \cdot b(k, i_1) + c(i_0, i_1)\]
For rank \(3\) arguments, the result is an approximation of the matrix \(r\) determined by
\[r(i_0, i_1, i_2) = \sum_{k = 0}^{k < L_2} a(i_0, i_1, k) \cdot b(i_0, k, i_2) + c(i_0, i_1, i_2)\]
Example
The following example shows a matrix multiply accumulate computation for rank \(2\) arguments:
namespace ct = ::cuda::tiles;
using i32x2x4 = ct::tile<int, ct::shape<2, 4>>;
using i32x4x2 = ct::tile<int, ct::shape<4, 2>>;
using i32x2x2 = ct::tile<int, ct::shape<2, 2>>;
auto lhs = ct::element_cast<float>(ct::iota<i32x2x4>());
auto rhs = ct::element_cast<float>(ct::iota<i32x4x2>());
auto acc = ct::element_cast<float>(ct::iota<i32x2x2>());
auto r = ct::mma(lhs, rhs, acc);
\[\begin{split}\begin{pmatrix}
0 & 1 & 2 & 3 \\
4 & 5 & 6 & 7 \\
\end{pmatrix} \times
\begin{pmatrix}
0 & 1 \\
2 & 3 \\
4 & 5 \\
6 & 7
\end{pmatrix} +
\begin{pmatrix}
0 & 1 \\
2 & 3
\end{pmatrix} \rightarrow
\begin{pmatrix}
28 & 35 \\
78 & 101
\end{pmatrix}\end{split}\]
Example
The following example shows a batched matrix multiply accumulate computation for rank \(3\) arguments:
namespace ct = ::cuda::tiles;
using i32x1x2x4 = ct::tile<int, ct::shape<1, 2, 4>>;
using i32x1x4x2 = ct::tile<int, ct::shape<1, 4, 2>>;
using i32x1x2x2 = ct::tile<int, ct::shape<1, 2, 2>>;
auto lhs = ct::element_cast<float>(
ct::cat<0>(ct::iota<i32x1x2x4>(), ct::iota<i32x1x2x4>()));
auto rhs = ct::element_cast<float>(
ct::cat<0>(ct::iota<i32x1x4x2>(), -ct::iota<i32x1x4x2>()));
auto acc = ct::element_cast<float>(
ct::cat<0>(ct::iota<i32x1x2x2>(), -ct::iota<i32x1x2x2>()));
auto r = ct::mma(lhs, rhs, acc);
\[\begin{split}\begin{pmatrix}
\begin{pmatrix}
0 & 1 & 2 & 3 \\
4 & 5 & 6 & 7 \\
\end{pmatrix} \\
\begin{pmatrix}
0 & 1 & 2 & 3 \\
4 & 5 & 6 & 7 \\
\end{pmatrix}
\end{pmatrix}
\times
\begin{pmatrix}
\begin{pmatrix}
0 & 1 \\
2 & 3 \\
4 & 5 \\
6 & 7
\end{pmatrix} \\
\begin{pmatrix}
0 & -1 \\
-2 & -3 \\
-4 & -5 \\
-6 & -7
\end{pmatrix}
\end{pmatrix} +
\begin{pmatrix}
\begin{pmatrix}
0 & 1 \\
2 & 3
\end{pmatrix} \\
\begin{pmatrix}
0 & -1 \\
-2 & -3
\end{pmatrix}
\end{pmatrix} \rightarrow
\begin{pmatrix}
\begin{pmatrix}
28 & 35 \\
78 & 101
\end{pmatrix} \\
\begin{pmatrix}
-28 & -35 \\
-78 & -101
\end{pmatrix}
\end{pmatrix}\end{split}\]
cuda::tiles::matmul
-
template<ct::numeric_tile L, ct::numeric_tile R>
requires ct::matmul_compatible<L, R> __tile__ ct::matmul_result_t<L, R> matmul(L lhs, R rhs) noexcept;
-
Performs matrix multiply on lhs and rhs. For
rank \(3\) arguments, the matrix multiply is performed
for each set of matrices along the first dimension of the operands.
If the operands have rank \(3\), the lhs undergoes
broadcast conversion to the shape \(\operatorname{max}(L_0, R_0) \times L_1 \times L_2\)
and rhs undergoes broadcast conversion to the
shape \(\operatorname{max}(L_0, R_0) \times R_1 \times R_2\).
Let \(a\) and \(b\) denote the converted operands lhs and rhs respectively.
For rank \(2\) arguments, the result is an approximation of the matrix \(r\) determined by
\[r(i_0, i_1) = \sum_{k = 0}^{k < L_1} a(i_0, k) \cdot b(k, i_1)\]
For rank \(3\) arguments, the result is an approximation of the matrix \(r\) determined by
\[r(i_0, i_1, i_2) = \sum_{k = 0}^{k < L_2} a(i_0, i_1, k) \cdot b(i_0, k, i_2)\]
Example
The following example shows a matrix multiply computation for rank \(2\) arguments:
namespace ct = ::cuda::tiles;
using i32x2x4 = ct::tile<int, ct::shape<2, 4>>;
using i32x4x2 = ct::tile<int, ct::shape<4, 2>>;
auto lhs = ct::element_cast<float>(ct::iota<i32x2x4>());
auto rhs = ct::element_cast<float>(ct::iota<i32x4x2>());
auto r = ct::matmul(lhs, rhs);
\[\begin{split}\begin{pmatrix}
0 & 1 & 2 & 3 \\
4 & 5 & 6 & 7 \\
\end{pmatrix} \times
\begin{pmatrix}
0 & 1 \\
2 & 3 \\
4 & 5 \\
6 & 7
\end{pmatrix} \rightarrow
\begin{pmatrix}
28 & 34 \\
76 & 98
\end{pmatrix}\end{split}\]
Example
The following example shows a batched matrix multiply computation for rank \(3\) arguments:
namespace ct = ::cuda::tiles;
using i32x1x2x4 = ct::tile<int, ct::shape<1, 2, 4>>;
using i32x1x4x2 = ct::tile<int, ct::shape<1, 4, 2>>;
auto lhs = ct::element_cast<float>(ct::cat<0>(ct::iota<i32x1x2x4>(), ct::iota<i32x1x2x4>()));
auto rhs = ct::element_cast<float>(ct::cat<0>(ct::iota<i32x1x4x2>(), -ct::iota<i32x1x4x2>()));
auto r = ct::matmul(lhs, rhs);
\[\begin{split}\begin{pmatrix}
\begin{pmatrix}
0 & 1 & 2 & 3 \\
4 & 5 & 6 & 7 \\
\end{pmatrix} \\
\begin{pmatrix}
0 & 1 & 2 & 3 \\
4 & 5 & 6 & 7 \\
\end{pmatrix}
\end{pmatrix}
\times
\begin{pmatrix}
\begin{pmatrix}
0 & 1 \\
2 & 3 \\
4 & 5 \\
6 & 7
\end{pmatrix} \\
\begin{pmatrix}
0 & -1 \\
-2 & -3 \\
-4 & -5 \\
-6 & -7
\end{pmatrix}
\end{pmatrix} \rightarrow
\begin{pmatrix}
\begin{pmatrix}
28 & 34 \\
76 & 98
\end{pmatrix} \\
\begin{pmatrix}
-28 & -34 \\
-76 & -98
\end{pmatrix}
\end{pmatrix}\end{split}\]
cuda::tiles::scaled_mma CUDA 13.5
-
template<ct::numeric_tile L, ct::numeric_tile LS, ct::numeric_tile R, ct::numeric_tile RS, ct::numeric_tile A>
requires /* atomic constraint */ __tile__ Acc scaled_mma(L lhs, LS lhsScale, R rhs, RS rhsScale, A acc) noexcept;
-
Performs block scaled matrix multiply and accumulate on the operands lhs, rhs, and
acc using the scale factors specified in lhsScale and rhsScale. For rank
\(3\) operands, the matrix multiply is batched along the first dimension and all inputs are
broadcasted to match the batch length of the accumulator.
The left and right matrices are subdivided into vectors. Each vector is associated with a
separate scaling factor which is used to scale the values in the vector before performing the
matrix multiply. The number of elements which correspond to a single scaling factor is called the
vector length.
For rank \(2\) arguments, the result is an approximation of the matrix \(r\) determined by
\[r(i_0, i_1) = \sum_{k = 0}^{k < L_1}
\text{lhs}(i_0, k) \cdot \text{lhsScale}\left(i_0, \left\lfloor\frac{k}{V}\right\rfloor\right) \cdot
\text{rhs}(k, i_1) \cdot \text{rhsScale}\left(\left\lfloor\frac{k}{V}\right\rfloor, i_1\right) +
\text{acc}(i_0, i_1)\]
where \(V = \frac{L_1}{LS_1}\) indicates the vector length.
For rank \(3\) operands, the inputs undergo broadcast conversion as follows:
lhs is broadcasted to shape \(A_0 \times L_1 \times L_2\)
lhsScale is broadcasted to shape \(A_0 \times LS_1 \times LS_2\)
rhs is broadcasted to shape \(A_0 \times R_1 \times R_2\)
rhsScale is broadcasted to shape \(A_0 \times RS_1 \times RS_2\)
The result is an approximation of the matrix \(r\) determined by the following equation
on the converted operands:
\[r(i_0, i_1, i_2) = \sum_{k = 0}^{k < L_2}
\text{lhs}(i_0, i_1, k) \cdot \text{lhsScale}\left(i_0, i_1, \left\lfloor\frac{k}{V}\right\rfloor\right) \cdot
\text{rhs}(i_0, k, i_2) \cdot \text{rhsScale}\left(i_0, \left\lfloor\frac{k}{V}\right\rfloor, i_2\right) +
\text{acc}(i_0, i_1, i_2)\]
where \(V = \frac{L_2}{LS_2}\) indicates the vector length.
The atomic constraint validates the following:
Shape Constraints
All arguments have the same rank and the rank is \(2\) or \(3\).
For rank \(2\) operands, the shape of each operand has the following format:
Shape of \(L\) |
Shape of \(LS\) |
Shape of \(R\) |
Shape of \(RS\) |
Shape of \(A\) |
\(N \times S \cdot V\) |
\(N \times S\) |
\(S \cdot V \times M\) |
\(S \times M\) |
\(N \times M\) |
for some integers \(N\), \(M\), \(S\), and \(V\). The value of \(V\) (the
vector size) must be consistent with the type constraint table below.
For rank \(3\) operands, the shape of each operand has the following format:
Shape of \(L\) |
Shape of \(LS\) |
Shape of \(R\) |
Shape of \(RS\) |
Shape of \(A\) |
\(B_0 \times N \times S \cdot V\) |
\(B_1 \times N \times S\) |
\(B_2 \times S \cdot V \times M\) |
\(B_3 \times S \times M\) |
\(B \times N \times M\) |
For some integers \(N\), \(M\), \(S\), \(V\), \(B_0\), \(B_1\),
\(B_2\), \(B_3\), and \(B\). The value of \(V\) (the vector size) must be
consistent with the type constraint table below. For each \(B_i\), either \(B_i = B\) or
\(B_i = 1\).
Type Constraints
The element types of \(L\) and \(R\) must be the same.
The element types of \(LS\) and \(RS\) must be the same.
The element type of \(A\) must be float.
The element types and vector size, \(V\), must be selected from one of the rows in the following table:
Example
The following example shows block scaled matrix multiply with a vector length of \(32\).
The lhs matrix is \(2 \times 64\) and has a total of four scale factors
corresponding to the four \(1 \times 32\) quadrants of lhs. The rhs matrix
is \(64 \times 2\) and has a total of four scale factors corresponding to the four
\(32 \times 1\) quadrants of rhs.
namespace ct = ::cuda::tiles;
using e5m2 = __nv_fp8_e5m2;
using e8m0 = __nv_fp8_e8m0;
e5m2 lhsData[2][64] = {};
lhsData[0][0] = e5m2(2.0f); lhsData[0][1] = e5m2(3.0f);
lhsData[1][0] = e5m2(4.0f); lhsData[1][1] = e5m2(6.0f);
lhsData[0][32] = e5m2(0.5f); lhsData[0][33] = e5m2(2.0f);
lhsData[1][32] = e5m2(-1.0f); lhsData[1][33] = e5m2(4.0f);
e8m0 lhsScaleData[2][2] = {
{e8m0(1.0f), e8m0(2.0f)},
{e8m0(4.0f), e8m0(8.0f)}
};
e5m2 rhsData[64][2] = {};
rhsData[0][0] = e5m2(-3.0f); rhsData[0][1] = e5m2(2.0f);
rhsData[1][0] = e5m2(0.5f); rhsData[1][1] = e5m2(1.5f);
rhsData[32][0] = e5m2(-2.0f); rhsData[32][1] = e5m2(2.0f);
rhsData[33][0] = e5m2(2.0f); rhsData[33][1] = e5m2(0.5f);
e8m0 rhsScaleData[2][2] = {
{e8m0(16.0f), e8m0(1.0f)},
{e8m0(0.5f), e8m0(4.0f)}
};
float accData[2][2] = {
{3.0f, -9.0f},
{5.0f, 7.0f}
};
using i32x2x64 = ct::tile<int, ct::shape<2, 64>>;
using i32x2x2 = ct::tile<int, ct::shape<2, 2>>;
using i32x64x2 = ct::tile<int, ct::shape<64, 2>>;
auto lhs = ct::load(&lhsData[0][0] + ct::iota<i32x2x64>());
auto lhsScale = ct::load(&lhsScaleData[0][0] + ct::iota<i32x2x2>());
auto rhs = ct::load(&rhsData[0][0] + ct::iota<i32x64x2>());
auto rhsScale = ct::load(&rhsScaleData[0][0] + ct::iota<i32x2x2>());
auto acc = ct::load(&accData[0][0] + ct::iota<i32x2x2>());
auto r = ct::scaled_mma(lhs, lhsScale, rhs, rhsScale, acc);
The lhs argument before scaling:
\[\begin{split}\text{lhs} = \begin{matrix}
\underbrace{\begin{array}{|c} \fcolorbox{black}{cyan}{$\begin{matrix} 2 & 3 & 0 & \cdots & 0 \end{matrix}$} \\
\fcolorbox{black}{violet}{$\begin{matrix} 4 & 6 & 0 & \cdots & 0 \end{matrix}$} \end{array}}_{\text{Block 0}}
&
\underbrace{\begin{array}{c|} \fcolorbox{black}{yellow}{$\begin{matrix}0.5 & 2 & 0 & \cdots & 0\end{matrix}$} \\
\fcolorbox{black}{lime}{$\begin{matrix} -1 & 4 & 0 & \cdots & 0 \end{matrix}$}\end{array}}_{\text{Block 1}}
\end{matrix}\end{split}\]
\[\begin{split}\text{lhsScale} = \begin{matrix}
\underbrace{\begin{array}{|c} \fcolorbox{black}{cyan}{1} \\ \fcolorbox{black}{violet}{4} \end{array}}_{\text{Block 0}} &
\underbrace{\begin{array}{c|} \fcolorbox{black}{yellow}{2} \\ \fcolorbox{black}{lime}{8} \end{array}}_{\text{Block 1}}
\end{matrix}\end{split}\]
The rhs argument before scaling:
\[\begin{split}\text{rhs} = \begin{matrix}
\begin{array}{|cc|}
\fcolorbox{black}{cyan}{$\begin{matrix}
-3 \\
0.5 \\
0 \\
\vdots \\
0
\end{matrix}$} &
\fcolorbox{black}{yellow}{$\begin{matrix}
2 \\
1.5 \\
0 \\
\vdots \\
0
\end{matrix}$}
\end{array} &
\left.\vphantom{\begin{matrix} 0 \\ 0 \\ 0 \\ 0 \\ 0 \end{matrix}}\right\} \text{Block 0} \\
\begin{array}{|cc|}
\fcolorbox{black}{violet}{$\begin{matrix}
-2 \\
2 \\
0 \\
\vdots \\
0
\end{matrix}$} &
\fcolorbox{black}{lime}{$\begin{matrix}
2 \\
0.5 \\
0 \\
\vdots \\
0
\end{matrix}$}
\end{array} &
\left.\vphantom{\begin{matrix} 0 \\ 0 \\ 0 \\ 0 \\ 0 \end{matrix}}\right\}\text{Block 1}
\end{matrix} \quad \quad \quad \text{rhsScale} = \begin{matrix}
\begin{array}{|cc|} \fcolorbox{black}{cyan}{$\hphantom{.}16$} & \fcolorbox{black}{yellow}{1} \end{array} &
\left.\vphantom{\begin{matrix} 0 \end{matrix}}\right\} \text{Block 0} \\
\begin{array}{|cc|} \fcolorbox{black}{violet}{$0.5$} & \fcolorbox{black}{lime}{4} \end{array} &
\left.\vphantom{\begin{matrix} 0 \end{matrix}}\right\}\text{Block 1}
\end{matrix}\end{split}\]
The matrix multiply accumulate after scaling is performed:
\[\begin{split}\begin{matrix}
\underbrace{\begin{array}{|ccccc} \hphantom{0}2 & \hphantom{0}3 & 0 & \cdots & 0 \\ 16 & 24 & 0 & \cdots & 0 \end{array}}_{\text{Block 0}} &
\underbrace{\begin{array}{ccccc|} \hphantom{0}1 & \hphantom{0}4 & 0 & \cdots & 0 \\ -8 & 32 & 0 & \cdots & 0 \end{array}}_{\text{Block 1}}
\end{matrix}
\quad \times \quad
\begin{matrix} \begin{array}{|cc|} -48 & 2 \\ 8 &
1.5 \\ 0 & 0 \\ \vdots & \vdots \\ 0 & 0 \end{array} &
\left.\vphantom{\begin{matrix} 0 \\ 0 \\ 0 \\ 0 \\ 0
\end{matrix}}\right\} \text{Block 0} \\
\begin{array}{|cc|} -1\hphantom{0} & \hphantom{0}8\hphantom{.} \\ 1 & 2 \\ 0 & 0 \\
\vdots & \vdots \\ 0 & 0 \end{array} &
\left.\vphantom{\begin{matrix} 0 \\ 0 \\ 0 \\ 0 \\ 0
\end{matrix}}\right\}\text{Block 1} \end{matrix}
\quad + \quad
\begin{pmatrix}
3 & -9 \\
5 & 7
\end{pmatrix}\end{split}\]
|