12#include <sycl/sycl.hpp>
17#include <src/sycl/kernel_attrs.hh>
22template <
typename T,
int NB>
31 return max_dim <= 8 ? 8 : max_dim <= 16 ? 16 : max_dim <= 32 ? 32 : 64;
34template <
typename T,
int NB>
40 int m,
int n,
int k) {
41 static_assert(!std::is_class_v<T>,
"real scalars only: std::complex's operator* is Annex G");
42 constexpr int kTpm = 2 * NB;
43 constexpr int kCpt = NB / 2;
45 constexpr int kLdb = NB + 1;
46 static_assert(kG >= 1 && kG * kTpm ==
kSmallWg,
"the work-group must hold whole matrices");
48 const int batch =
static_cast<int>(A.
batch_size());
49 const int num_wg = (batch + kG - 1) / kG;
54 const std::ptrdiff_t lda = A.
ld(), ldb = B.
ld(), ldc = C.
ld();
58 const bool beta_zero = (beta == T(0));
60 ctx->submit([&](sycl::handler& h) {
61 sycl::local_accessor<T, 1> slm(sycl::range<1>(
static_cast<std::size_t
>(kG) * NB * kLdb), h);
62 batchlas::impl::parallel_for<GemmSmallBatchedKernel<T, NB>>(h,
63 sycl::nd_range<1>(sycl::range<1>(
static_cast<std::size_t
>(num_wg) *
kSmallWg),
65 [=](sycl::nd_item<1> it) BATCHLAS_REQD_SG_SIZE(32) {
66 const int t =
static_cast<int>(it.get_local_id(0));
67 const int slot = t / kTpm;
68 const int tt = t % kTpm;
69 const int mat =
static_cast<int>(it.get_group(0)) * kG + slot;
71 const bool live = mat < batch;
72 const std::ptrdiff_t bi = live ? mat : 0;
73 const int r = tt % NB;
74 const int c0 = (tt / NB) * kCpt;
75 T*
const sB = &slm[0] +
static_cast<std::ptrdiff_t
>(slot) * NB * kLdb;
76 const T*
const Ab = a_ptr + bi * sa;
77 const T*
const Bb = b_ptr + bi * sb;
81 for (
int e = tt; e < NB * NB; e += kTpm) {
82 const int s0 = e % NB, s1 = e / NB;
83 const int l = tb ? s1 : s0;
84 const int c = tb ? s0 : s1;
86 if (live && l < k && c < n) v = Bb[s0 + s1 * ldb];
93 for (
int l = 0; l < NB; ++l) {
95 if (live && r < m && l < k) v = ta ? Ab[l + r * lda] : Ab[r + l * lda];
98 it.barrier(sycl::access::fence_space::local_space);
102 for (
int j = 0; j < kCpt; ++j) acc[j] = T(0);
104 for (
int l = 0; l < NB; ++l) {
105 if (l >= k)
continue;
107 for (
int j = 0; j < kCpt; ++j) acc[j] += a[l] * sB[l + (c0 + j) * kLdb];
111 T*
const Cb = c_ptr + bi * sc;
113 for (
int j = 0; j < kCpt; ++j) {
114 const int c = c0 + j;
116 T*
const dst = Cb + r + c * ldc;
117 *dst = beta_zero ? alpha * acc[j] : alpha * acc[j] + beta * *dst;
126template <
typename T,
int NB,
bool PrefetchC>
137template <
typename T,
int NB,
bool PrefetchC>
142 T alpha, T beta,
int m,
int n,
int k) {
144 constexpr int kTr = NB / 4;
145 constexpr int kWg = kTr * kTr;
146 constexpr int kLd = NB + 4;
147 static_assert(NB % 4 == 0,
"4x4 lane tiles");
149 const int batch =
static_cast<int>(A.
batch_size());
153 const std::ptrdiff_t lda = A.
ld(), ldb = B.
ld(), ldc = C.
ld();
155 const bool beta_zero = (beta == T(0));
157 ctx->submit([&](sycl::handler& h) {
158 sycl::local_accessor<T, 1> slm(sycl::range<1>(
static_cast<std::size_t
>(2 * NB * kLd)), h);
159 batchlas::impl::parallel_for<GemmSmallTiledKernel<T, NB, PrefetchC>>(h,
160 sycl::nd_range<1>(sycl::range<1>(
static_cast<std::size_t
>(batch) * kWg),
161 sycl::range<1>(kWg)),
162 [=](sycl::nd_item<1> it) {
163 const int t =
static_cast<int>(it.get_local_id(0));
164 const std::ptrdiff_t bi =
static_cast<std::ptrdiff_t
>(it.get_group(0));
165 const int kp = (k + 3) & ~3;
166 T*
const sA = &slm[0];
167 T*
const sB = sA + NB * kLd;
168 const T*
const Ab = a_ptr + bi * sa;
169 const T*
const Bb = b_ptr + bi * sb;
170 T*
const Cb = c_ptr + bi * sc;
172 for (
int e = t; e < NB * kp; e += kWg) {
173 const int r = e % NB, l = e / NB;
174 sA[l * kLd + r] = (r < m && l < k) ? Ab[r + l * lda] : T(0);
176 for (
int e = t; e < kp * NB; e += kWg) {
177 const int l = e % kp, c = e / kp;
178 sB[c * kLd + l] = (l < k && c < n) ? Bb[l + c * ldb] : T(0);
180 const int tr = t % kTr, tc = t / kTr;
182 if constexpr (PrefetchC) {
184 for (
int j = 0; j < 4; ++j) {
186 for (
int i = 0; i < 4; ++i) {
187 const int r = tr * 4 + i, c = tc * 4 + j;
188 prior[i][j] = (r < m && c < n) ? Cb[r + c * ldc] : T(0);
192 it.barrier(sycl::access::fence_space::local_space);
196 for (
int i = 0; i < 4; ++i) {
198 for (
int j = 0; j < 4; ++j) acc[i][j] = T(0);
200 for (
int l0 = 0; l0 < kp; l0 += 4) {
203 for (
int j = 0; j < 4; ++j) {
204 bv[j] = *
reinterpret_cast<const V4*
>(&sB[(tc * 4 + j) * kLd + l0]);
207 for (
int ll = 0; ll < 4; ++ll) {
208 const V4 av = *
reinterpret_cast<const V4*
>(&sA[(l0 + ll) * kLd + tr * 4]);
210 for (
int i = 0; i < 4; ++i) {
212 for (
int j = 0; j < 4; ++j) acc[i][j] += av[i] * bv[j][ll];
218 for (
int j = 0; j < 4; ++j) {
219 const int c = tc * 4 + j;
221 for (
int i = 0; i < 4; ++i) {
222 const int r = tr * 4 + i;
223 if (r < m && c < n) {
224 T*
const dst = Cb + r + c * ldc;
225 if constexpr (PrefetchC) {
226 *dst = alpha * acc[i][j] + beta * prior[i][j];
228 *dst = beta_zero ? alpha * acc[i][j] : alpha * acc[i][j] + beta * *dst;
238template <
typename T,
int NB>
243 T alpha, T beta,
int m,
int n,
int k) {
245 return launch_small_tiled<T, NB, false>(ctx, A, B, C, alpha, beta, m, n, k);
247 return launch_small_tiled<T, NB, true>(ctx, A, B, C, alpha, beta, m, n, k);
256 int m,
int n,
int k) {
257 const int max_dim = m > n ? (m > k ? m : k) : (n > k ? n : k);
258 if constexpr (std::is_same_v<T, float>) {
261 return max_dim <= 48 ? small_tiled<T, 48>(ctx, A, B, C, alpha, beta, m, n, k)
267 return launch_small_batched<T, 8>(ctx, A, B, C, alpha, beta, transA, transB, m, n, k);
269 return launch_small_batched<T, 16>(ctx, A, B, C, alpha, beta, transA, transB, m, n, k);
271 return launch_small_batched<T, 32>(ctx, A, B, C, alpha, beta, transA, transB, m, n, k);
273 return launch_small_batched<T, 64>(ctx, A, B, C, alpha, beta, transA, transB, m, n, k);
Non-owning view of a batch of matrices: the argument type of every entry point.
T * data_ptr() const
Base pointer of item 0.
int ld() const
Leading dimension: element distance between columns.
int stride() const
Batch stride: element distance between items.
int batch_size() const
Number of batch items.
Enumerations, scalar traits and scalar concepts shared by every public header.
Transpose
The operator op() applied to an operand: op(A) = A, A^T or A^H.
The batched containers: Matrix (owning), MatrixView (non-owning), KernelMatrixView (device-side),...
Event launch_small_batched(Queue &ctx, const MatrixView< T, MatrixFormat::Dense > &A, const MatrixView< T, MatrixFormat::Dense > &B, const MatrixView< T, MatrixFormat::Dense > &C, T alpha, T beta, Transpose transA, Transpose transB, int m, int n, int k)
constexpr int kSmallTiledMaxDim
int small_bucket(int max_dim)
Event launch_small_tiled(Queue &ctx, const MatrixView< T, MatrixFormat::Dense > &A, const MatrixView< T, MatrixFormat::Dense > &B, const MatrixView< T, MatrixFormat::Dense > &C, T alpha, T beta, int m, int n, int k)
Event small_batched(Queue &ctx, const MatrixView< T, MatrixFormat::Dense > &A, const MatrixView< T, MatrixFormat::Dense > &B, const MatrixView< T, MatrixFormat::Dense > &C, T alpha, T beta, Transpose transA, Transpose transB, int m, int n, int k)
constexpr int kSmallMaxDim
Event small_tiled(Queue &ctx, const MatrixView< T, MatrixFormat::Dense > &A, const MatrixView< T, MatrixFormat::Dense > &B, const MatrixView< T, MatrixFormat::Dense > &C, T alpha, T beta, int m, int n, int k)
T __attribute__((ext_vector_type(4))) Vec4
Completion handle of enqueued work, returned by every entry point.
Where BatchLAS work runs: a device, a backend, an ordered SYCL queue and a workspace arena.
Event get_event() const
An Event that completes after everything enqueued on this Queue so far.
batchlas::Device, batchlas::Event and batchlas::Queue: where work runs and how it is ordered.