139 BATCHLAS_KERNEL_TRACE_SCOPE(
146 using D =
typename DevMap<T>::type;
147 static_assert(
sizeof(D) ==
sizeof(T),
"device scalar must be layout-compatible");
149 constexpr int TileM = Tile::M;
150 constexpr int TileN = Tile::N;
151 constexpr int TileK = Tile::K;
152 constexpr int TT = Tile::TT;
153 constexpr int LocalRows = Tile::LocalRows;
154 constexpr int LocalCols = Tile::LocalCols;
155 constexpr int AStride = Tile::AStride;
156 constexpr int BStride = Tile::BStride;
158 constexpr int VecN = Vec16<D>::N;
159 constexpr int Wb = (VecN < TT) ? VecN : TT;
160 constexpr int NB = TT / Wb;
161 constexpr int MSep = TileM / NB;
162 constexpr int NSep = TileN / NB;
163 constexpr int Chunks = Tile::PerThreadA / VecN;
164 constexpr int AGranPerRow = TileM / VecN;
166 static_assert(TileM % VecN == 0,
"A tile must split into whole granules");
167 static_assert(AGranPerRow * TileK == Chunks * Tile::Threads,
"A granule handout");
168 static_assert(TT % Wb == 0,
"thread tile must split into whole bands");
169 static_assert(Tile::PerThreadA % VecN == 0,
"staging must be whole granules");
170 static_assert(Tile::PerThreadA == Tile::PerThreadB,
"Chunks is shared by A and B staging");
171 static_assert(MSep % VecN == 0 && NSep % VecN == 0,
"band base must stay aligned");
172 static_assert(AStride % VecN == 0 && BStride % VecN == 0,
"shared stride alignment");
174 const int m =
static_cast<int>(A.
rows());
175 const int k =
static_cast<int>(A.
cols());
176 const int n =
static_cast<int>(B.
cols());
178 const int gm_tiles = (m + TileM - 1) / TileM;
179 const int gn_tiles = (n + TileN - 1) / TileN;
182 const sycl::range<3> local(1, LocalCols, LocalRows);
183 const sycl::range<3> global(
static_cast<size_t>(A.
batch_size()),
184 static_cast<size_t>(gn_tiles) * LocalCols,
185 static_cast<size_t>(gm_tiles) * LocalRows);
187 ctx->submit([&](sycl::handler& h) {
188 sycl::local_accessor<Vec16<D>, 1> tile_a(sycl::range<1>(TileK * AStride / VecN), h);
189 sycl::local_accessor<Vec16<D>, 1> tile_b(sycl::range<1>(TileK * BStride / VecN), h);
191 const D* a_ptr =
reinterpret_cast<const D*
>(A.
data_ptr());
192 const D* b_ptr =
reinterpret_cast<const D*
>(B.
data_ptr());
193 D* c_ptr =
reinterpret_cast<D*
>(C.
data_ptr());
194 const int lda =
static_cast<int>(A.
ld());
195 const int ldb =
static_cast<int>(B.
ld());
196 const int ldc =
static_cast<int>(C.
ld());
197 const int stride_a =
static_cast<int>(A.
stride());
198 const int stride_b =
static_cast<int>(B.
stride());
199 const int stride_c =
static_cast<int>(C.
stride());
200 const int batch =
static_cast<int>(A.
batch_size());
204 __builtin_memcpy(&alpha_d, &alpha,
sizeof(D));
205 __builtin_memcpy(&beta_d, &beta,
sizeof(D));
207 batchlas::impl::parallel_for<GemmRegister64x64K16WideKernel<T, AlignedFastPath>>(h,
208 sycl::nd_range<3>(global, local), [=](sycl::nd_item<3> item) {
209 const int bid =
static_cast<int>(item.get_group(0));
217 const int ty =
static_cast<int>(item.get_local_id(2));
218 const int tx =
static_cast<int>(item.get_local_id(1));
219 const int tid = tx * LocalRows + ty;
221 const int m0 =
static_cast<int>(item.get_group(2)) * TileM;
222 const int n0 =
static_cast<int>(item.get_group(1)) * TileN;
224 const D* Ab = a_ptr +
static_cast<std::ptrdiff_t
>(bid) * stride_a;
225 const D* Bb = b_ptr +
static_cast<std::ptrdiff_t
>(bid) * stride_b;
226 D* Cb = c_ptr +
static_cast<std::ptrdiff_t
>(bid) * stride_c;
228 D* sa =
reinterpret_cast<D*
>(
229 tile_a.template get_multi_ptr<sycl::access::decorated::no>().get());
230 D* sb =
reinterpret_cast<D*
>(
231 tile_b.template get_multi_ptr<sycl::access::decorated::no>().get());
236 for (
int i = 0; i < TT; ++i) {
238 for (
int j = 0; j < TT; ++j) {
250 for (
int c = 0; c < Chunks; ++c) {
251 const int g = tid + c * Tile::Threads;
252 a_gm[c] = (g % AGranPerRow) * VecN;
253 a_gk[c] = g / AGranPerRow;
258 const int b_k = (tid % (TileK / Tile::PerThreadB)) * Tile::PerThreadB;
259 const int b_n = tid / (TileK / Tile::PerThreadB);
261 for (
int k0 = 0; k0 < k; k0 += TileK) {
262 if constexpr (AlignedFastPath) {
264 for (
int c = 0; c < Chunks; ++c) {
266 &sa[a_gk[c] * AStride + a_gm[c]],
267 raw16_load(Ab + (m0 + a_gm[c]) +
268 static_cast<std::ptrdiff_t
>(k0 + a_gk[c]) * lda));
270 alignas(16) D vb[Tile::PerThreadB];
272 for (
int c = 0; c < Chunks; ++c) {
273 raw16_store(&vb[c * VecN],
274 raw16_load(Bb + (k0 + b_k + c * VecN) +
275 static_cast<std::ptrdiff_t
>(n0 + b_n) * ldb));
278 for (
int i = 0; i < Tile::PerThreadB; ++i) {
279 sb[(b_k + i) * BStride + b_n] = vb[i];
286 for (
int c = 0; c < Chunks; ++c) {
287 const int gk_a = k0 + a_gk[c];
289 for (
int e = 0; e < VecN; ++e) {
290 const int gm = m0 + a_gm[c] + e;
291 sa[a_gk[c] * AStride + a_gm[c] + e] =
293 ? Ab[gm +
static_cast<std::ptrdiff_t
>(gk_a) * lda]
297 const int gn_b = n0 + b_n;
299 for (
int i = 0; i < Tile::PerThreadB; ++i) {
300 const int gk = k0 + b_k + i;
301 sb[(b_k + i) * BStride + b_n] =
303 ? Bb[gk +
static_cast<std::ptrdiff_t
>(gn_b) * ldb]
307 item.barrier(sycl::access::fence_space::local_space);
310 for (
int kk = 0; kk < TileK; ++kk) {
314 for (
int band = 0; band < NB; ++band) {
316 vec_ref(&sa[kk * AStride + band * MSep + ty * Wb]);
318 for (
int w = 0; w < Wb; ++w) {
319 af[band * Wb + w] = t.v[w];
323 for (
int band = 0; band < NB; ++band) {
325 vec_ref(&sb[kk * BStride + band * NSep + tx * Wb]);
327 for (
int w = 0; w < Wb; ++w) {
328 bf[band * Wb + w] = t.v[w];
332 for (
int i = 0; i < TT; ++i) {
334 for (
int j = 0; j < TT; ++j) {
335 fma_acc(accum[i][j], af[i], bf[j]);
339 item.barrier(sycl::access::fence_space::local_space);
344 const bool beta_zero = dev_is_zero(beta_d);
346 for (
int bm = 0; bm < NB; ++bm) {
347 const int gm = m0 + bm * MSep + ty * Wb;
349 for (
int j = 0; j < TT; ++j) {
350 const int bn = j / Wb;
351 const int w_n = j % Wb;
352 const int gn = n0 + bn * NSep + tx * Wb + w_n;
353 if constexpr (AlignedFastPath) {
354 D* p = &Cb[gm +
static_cast<std::ptrdiff_t
>(gn) * ldc];
358 for (
int w = 0; w < Wb; ++w) {
360 lin_epi(alpha_d, D{}, accum[bm * Wb + w][j], D{});
363 const Vec16<D> prior = vec_ref(
const_cast<const D*
>(p));
365 for (
int w = 0; w < Wb; ++w) {
366 out.v[w] = lin_epi(alpha_d, beta_d,
367 accum[bm * Wb + w][j], prior.v[w]);
376 for (
int w = 0; w < Wb; ++w) {
377 const int row = gm + w;
381 D* p = &Cb[row +
static_cast<std::ptrdiff_t
>(gn) * ldc];
382 const D prior = beta_zero ? D{} : *p;
383 *p = lin_epi(alpha_d, beta_zero ? D{} : beta_d,
384 accum[bm * Wb + w][j], prior);