120 const char* kernel_trace_name) {
123 BATCHLAS_KERNEL_TRACE_SCOPE(kernel_trace_name);
125 static_assert(TileM % ThreadTileRows == 0,
"TileM must divide evenly by ThreadTileRows");
126 static_assert(TileN % ThreadTileCols == 0,
"TileN must divide evenly by ThreadTileCols");
127 static_assert(TileM * TileK >= Policy::ThreadsPerGroup,
"TileA footprint must cover all threads");
128 static_assert(TileN * TileK >= Policy::ThreadsPerGroup,
"TileB footprint must cover all threads");
129 static_assert(TileM % VecA == 0 && TileK % VecA == 0,
"VecA must divide TileM and TileK");
130 static_assert(TileN % VecB == 0 && TileK % VecB == 0,
"VecB must divide TileN and TileK");
131 static_assert(UnrollK >= 1,
"UnrollK must be positive");
132 static_assert(Stages >= 1,
"Stages must be positive");
133 static_assert(Stages <= 2,
"Register-tiled GEMM only supports one-stage and two-stage pipelines");
135 "Aligned fast path is only supported for NN kernels");
139 static_cast<void>(_);
143 const sycl::range<3> local(1, Policy::LocalRows, Policy::LocalCols);
146 const sycl::range<3> global(
static_cast<size_t>(A.
batch_size()),
147 static_cast<size_t>(group_rows * Policy::LocalRows),
148 static_cast<size_t>(group_cols * Policy::LocalCols));
150 ctx->submit([&](sycl::handler& h) {
151 sycl::local_accessor<T, 1> tile_a(sycl::range<1>(Policy::Stages * Policy::StageASize), h);
152 sycl::local_accessor<T, 1> tile_b(sycl::range<1>(Policy::Stages * Policy::StageBSize), h);
157 const int lda = A.
ld();
158 const int ldb = B.
ld();
159 const int ldc = C.
ld();
160 const int stride_a = A.
stride();
161 const int stride_b = B.
stride();
162 const int stride_c = C.
stride();
164 const bool packet_a = use_packet_a;
165 const bool packet_b = use_packet_b;
167 batchlas::impl::parallel_for<
168 GemmRegisterTiledKernel<T, TileM, TileN, TileK, ThreadTileRows, ThreadTileCols, VecA, VecB, UnrollK, Stages, AlignedFastPath, OpA, OpB>>(h,
169 sycl::nd_range<3>(global, local), [=](sycl::nd_item<3> item) {
170 const int bid =
static_cast<int>(item.get_group(0));
171 const int local_row =
static_cast<int>(item.get_local_id(1));
172 const int local_col =
static_cast<int>(item.get_local_id(2));
177 const int row_base =
static_cast<int>(item.get_group(1)) * TileM + local_row * ThreadTileRows;
178 const int col_base =
static_cast<int>(item.get_group(2)) * TileN + local_col * ThreadTileCols;
179 const int linear_tid = local_row * Policy::LocalCols + local_col;
182 const T*
const a_item = a_ptr +
static_cast<std::ptrdiff_t
>(bid) * stride_a;
183 const T*
const b_item = b_ptr +
static_cast<std::ptrdiff_t
>(bid) * stride_b;
184 T*
const c_item = c_ptr +
static_cast<std::ptrdiff_t
>(bid) * stride_c;
185 constexpr int batch_a = 0, batch_b = 0, batch_c = 0;
186 const int group_row =
static_cast<int>(item.get_group(1));
187 const int group_col =
static_cast<int>(item.get_group(2));
188 T accum[ThreadTileRows][ThreadTileCols];
189 for (
int i = 0; i < ThreadTileRows; ++i) {
190 for (
int j = 0; j < ThreadTileCols; ++j) {
195 auto load_stage = [&](
int kk0,
int stage) {
196 T* tile_a_stage = &tile_a[stage * Policy::StageASize];
197 T* tile_b_stage = &tile_b[stage * Policy::StageBSize];
201 constexpr int APacketsPerCol = TileM / VecA;
202 constexpr int APacketCount = APacketsPerCol * TileK;
203 constexpr int APacketIterations = (APacketCount + Policy::ThreadsPerGroup - 1) / Policy::ThreadsPerGroup;
205 constexpr int iteration = iter;
206 const int packet = linear_tid + iteration * Policy::ThreadsPerGroup;
207 if (packet < APacketCount) {
208 const int a_row = (packet % APacketsPerCol) * VecA;
209 const int a_col = packet / APacketsPerCol;
210 const int logical_a_row = group_row * TileM + a_row;
211 const int logical_a_col = kk0 + a_col;
212 const int base = batch_a + logical_a_col * lda + logical_a_row;
214 for (
int lane = 0; lane < VecA; ++lane) {
215 tile_a_stage[a_col * Policy::TileAStride + a_row + lane] = packet_values[lane];
220 constexpr int BPacketsPerCol = TileK / VecB;
221 constexpr int BPacketCount = TileN * BPacketsPerCol;
222 constexpr int BPacketIterations = (BPacketCount + Policy::ThreadsPerGroup - 1) / Policy::ThreadsPerGroup;
224 constexpr int iteration = iter;
225 const int packet = linear_tid + iteration * Policy::ThreadsPerGroup;
226 if (packet < BPacketCount) {
227 const int b_row = (packet % BPacketsPerCol) * VecB;
228 const int b_col = packet / BPacketsPerCol;
229 const int logical_b_row = kk0 + b_row;
230 const int logical_b_col = group_col * TileN + b_col;
231 const int base = batch_b + logical_b_col * ldb + logical_b_row;
233 for (
int lane = 0; lane < VecB; ++lane) {
234 tile_b_stage[b_col * Policy::TileBStride + b_row + lane] = packet_values[lane];
244 constexpr int PacketsPerCol = TileM / VecA;
245 constexpr int PacketCount = PacketsPerCol * TileK;
247 for (
int packet = linear_tid; packet < PacketCount; packet += Policy::ThreadsPerGroup) {
248 const int a_row = (packet % PacketsPerCol) * VecA;
249 const int a_col = packet / PacketsPerCol;
250 const int logical_a_row = group_row * TileM + a_row;
251 const int logical_a_col = kk0 + a_col;
252 if (logical_a_row + (VecA - 1) < m && logical_a_col < k) {
253 const int base = batch_a + logical_a_col * lda + logical_a_row;
255 for (
int lane = 0; lane < VecA; ++lane) {
256 tile_a_stage[a_col * Policy::TileAStride + a_row + lane] = packet_values[lane];
259 for (
int lane = 0; lane < VecA; ++lane) {
260 const int row = logical_a_row + lane;
261 tile_a_stage[a_col * Policy::TileAStride + a_row + lane] = (row < m && logical_a_col < k)
268 constexpr int PacketsPerRow = TileK / VecA;
269 constexpr int PacketCount = TileM * PacketsPerRow;
271 for (
int packet = linear_tid; packet < PacketCount; packet += Policy::ThreadsPerGroup) {
272 const int a_row = packet / PacketsPerRow;
273 const int a_col = (packet % PacketsPerRow) * VecA;
274 const int logical_a_row = group_row * TileM + a_row;
275 const int logical_a_col = kk0 + a_col;
276 if (logical_a_row < m && logical_a_col + (VecA - 1) < k) {
277 const int base = batch_a + logical_a_row * lda + logical_a_col;
279 for (
int lane = 0; lane < VecA; ++lane) {
280 tile_a_stage[(a_col + lane) * Policy::TileAStride + a_row] = packet_values[lane];
283 for (
int lane = 0; lane < VecA; ++lane) {
284 const int col = logical_a_col + lane;
285 tile_a_stage[(a_col + lane) * Policy::TileAStride + a_row] = (logical_a_row < m && col < k)
293 for (
int index = linear_tid; index < TileM * TileK; index += Policy::ThreadsPerGroup) {
294 const int a_row = index % TileM;
295 const int a_col = index / TileM;
296 const int logical_a_row = group_row * TileM + a_row;
297 const int logical_a_col = kk0 + a_col;
298 tile_a_stage[a_col * Policy::TileAStride + a_row] = (logical_a_row < m && logical_a_col < k)
306 constexpr int PacketsPerCol = TileK / VecB;
307 constexpr int PacketCount = TileN * PacketsPerCol;
309 for (
int packet = linear_tid; packet < PacketCount; packet += Policy::ThreadsPerGroup) {
310 const int b_row = (packet % PacketsPerCol) * VecB;
311 const int b_col = packet / PacketsPerCol;
312 const int logical_b_row = kk0 + b_row;
313 const int logical_b_col = group_col * TileN + b_col;
314 if (logical_b_row + (VecB - 1) < k && logical_b_col < n) {
315 const int base = batch_b + logical_b_col * ldb + logical_b_row;
317 for (
int lane = 0; lane < VecB; ++lane) {
318 tile_b_stage[b_col * Policy::TileBStride + b_row + lane] = packet_values[lane];
321 for (
int lane = 0; lane < VecB; ++lane) {
322 const int row = logical_b_row + lane;
323 tile_b_stage[b_col * Policy::TileBStride + b_row + lane] = (row < k && logical_b_col < n)
330 constexpr int PacketsPerRow = TileN / VecB;
331 constexpr int PacketCount = TileK * PacketsPerRow;
333 for (
int packet = linear_tid; packet < PacketCount; packet += Policy::ThreadsPerGroup) {
334 const int b_row = packet / PacketsPerRow;
335 const int b_col = (packet % PacketsPerRow) * VecB;
336 const int logical_b_row = kk0 + b_row;
337 const int logical_b_col = group_col * TileN + b_col;
338 if (logical_b_row < k && logical_b_col + (VecB - 1) < n) {
339 const int base = batch_b + logical_b_row * ldb + logical_b_col;
341 for (
int lane = 0; lane < VecB; ++lane) {
342 tile_b_stage[(b_col + lane) * Policy::TileBStride + b_row] = packet_values[lane];
345 for (
int lane = 0; lane < VecB; ++lane) {
346 const int col = logical_b_col + lane;
347 tile_b_stage[(b_col + lane) * Policy::TileBStride + b_row] = (logical_b_row < k && col < n)
355 for (
int index = linear_tid; index < TileN * TileK; index += Policy::ThreadsPerGroup) {
356 const int b_row = index % TileK;
357 const int b_col = index / TileK;
358 const int logical_b_row = kk0 + b_row;
359 const int logical_b_col = group_col * TileN + b_col;
360 tile_b_stage[b_col * Policy::TileBStride + b_row] = (logical_b_row < k && logical_b_col < n)
366 for (
int index = linear_tid; index < TileM * TileK; index += Policy::ThreadsPerGroup) {
367 const int a_row = index % TileM;
368 const int a_col = index / TileM;
369 const int logical_a_row = group_row * TileM + a_row;
370 const int logical_a_col = kk0 + a_col;
371 tile_a_stage[a_col * Policy::TileAStride + a_row] = (logical_a_row < m && logical_a_col < k)
376 for (
int index = linear_tid; index < TileN * TileK; index += Policy::ThreadsPerGroup) {
377 const int b_row = index % TileK;
378 const int b_col = index / TileK;
379 const int logical_b_row = kk0 + b_row;
380 const int logical_b_col = group_col * TileN + b_col;
381 tile_b_stage[b_col * Policy::TileBStride + b_row] = (logical_b_row < k && logical_b_col < n)
388 auto accumulate_stage = [&](
const T* tile_a_stage,
const T* tile_b_stage,
int kk0) {
389 if constexpr (AlignedFastPath) {
390 for (
int t0 = 0; t0 < TileK; t0 += UnrollK) {
391 for (
int unroll = 0; unroll < UnrollK; ++unroll) {
392 const int t = t0 + unroll;
393 T a_frag[ThreadTileRows];
394 T b_frag[ThreadTileCols];
395 for (
int i = 0; i < ThreadTileRows; ++i) {
396 a_frag[i] = tile_a_stage[t * Policy::TileAStride + local_row * ThreadTileRows + i];
398 for (
int j = 0; j < ThreadTileCols; ++j) {
399 b_frag[j] = tile_b_stage[(local_col * ThreadTileCols + j) * Policy::TileBStride + t];
401 for (
int i = 0; i < ThreadTileRows; ++i) {
402 for (
int j = 0; j < ThreadTileCols; ++j) {
403 accum[i][j] += a_frag[i] * b_frag[j];
409 for (
int t0 = 0; t0 < TileK && kk0 + t0 < k; t0 += UnrollK) {
410 for (
int unroll = 0; unroll < UnrollK && t0 + unroll < TileK && kk0 + t0 + unroll < k; ++unroll) {
411 const int t = t0 + unroll;
412 T a_frag[ThreadTileRows];
413 T b_frag[ThreadTileCols];
414 for (
int i = 0; i < ThreadTileRows; ++i) {
415 a_frag[i] = tile_a_stage[t * Policy::TileAStride + local_row * ThreadTileRows + i];
417 for (
int j = 0; j < ThreadTileCols; ++j) {
418 b_frag[j] = tile_b_stage[(local_col * ThreadTileCols + j) * Policy::TileBStride + t];
420 for (
int i = 0; i < ThreadTileRows; ++i) {
421 for (
int j = 0; j < ThreadTileCols; ++j) {
422 accum[i][j] += a_frag[i] * b_frag[j];
431 if constexpr (Policy::Stages == 1) {
432 for (
int tile_idx = 0; tile_idx < tile_count; ++tile_idx) {
433 const int kk0 = tile_idx * TileK;
435 item.barrier(sycl::access::fence_space::local_space);
436 accumulate_stage(&tile_a[0], &tile_b[0], kk0);
437 item.barrier(sycl::access::fence_space::local_space);
440 if constexpr (AlignedFastPath) {
441 if (tile_count > 0) {
443 item.barrier(sycl::access::fence_space::local_space);
445 for (
int tile_idx = 0; tile_idx + 1 < tile_count; ++tile_idx) {
446 const int current_stage = tile_idx & 1;
447 const int next_stage = current_stage ^ 1;
448 load_stage((tile_idx + 1) * TileK, next_stage);
449 accumulate_stage(&tile_a[current_stage * Policy::StageASize],
450 &tile_b[current_stage * Policy::StageBSize],
452 item.barrier(sycl::access::fence_space::local_space);
455 const int final_stage = (tile_count - 1) & 1;
456 accumulate_stage(&tile_a[final_stage * Policy::StageASize],
457 &tile_b[final_stage * Policy::StageBSize],
458 (tile_count - 1) * TileK);
459 item.barrier(sycl::access::fence_space::local_space);
463 item.barrier(sycl::access::fence_space::local_space);
465 for (
int tile_idx = 0; tile_idx < tile_count; ++tile_idx) {
466 const int kk0 = tile_idx * TileK;
467 const int current_stage = tile_idx & 1;
468 const int next_tile_idx = tile_idx + 1;
470 if (next_tile_idx < tile_count) {
471 load_stage(next_tile_idx * TileK, current_stage ^ 1);
474 accumulate_stage(&tile_a[current_stage * Policy::StageASize],
475 &tile_b[current_stage * Policy::StageBSize],
477 item.barrier(sycl::access::fence_space::local_space);
482 if constexpr (AlignedFastPath) {
483 for (
int i = 0; i < ThreadTileRows; ++i) {
484 const int row = row_base + i;
485 for (
int j = 0; j < ThreadTileCols; ++j) {
486 const int col = col_base + j;
491 for (
int i = 0; i < ThreadTileRows; ++i) {
492 const int row = row_base + i;
496 for (
int j = 0; j < ThreadTileCols; ++j) {
497 const int col = col_base + j;