101 BATCHLAS_KERNEL_TRACE_SCOPE(trace);
105 using D =
typename DevMap<T>::type;
106 static_assert(
sizeof(D) ==
sizeof(T),
"device scalar must be layout-compatible");
108 constexpr int TileM = P.M;
109 constexpr int TileN = P.N;
110 constexpr int TileK = P.K;
111 constexpr int TTM = P.TTM;
112 constexpr int TTN = P.TTN;
118 constexpr int LocalRows = TileM / TTM;
119 constexpr int LocalCols = TileN / TTN;
120 constexpr int Threads = LocalRows * LocalCols;
124 constexpr int AStride = TileM;
125 constexpr int BStride = TileN;
127 constexpr int VecN = Vec16<D>::N;
128 constexpr int WbM = (VecN < TTM) ? VecN : TTM;
129 constexpr int WbN = (VecN < TTN) ? VecN : TTN;
130 constexpr int NBM = TTM / WbM;
131 constexpr int NBN = TTN / WbN;
132 constexpr int MSep = TileM / NBM;
133 constexpr int NSep = TileN / NBN;
135 static_assert(TileM % TTM == 0 && TileN % TTN == 0,
136 "macro tile must split into thread tiles");
137 static_assert(TTM % WbM == 0 && TTN % WbN == 0,
138 "thread tile must split into whole bands");
139 static_assert(Threads >= 32 && Threads <= 1024,
"work-group size out of range");
140 static_assert((TileK * AStride) % VecN == 0 && (TileK * BStride) % VecN == 0,
141 "shared tile must be a whole number of 16-byte granules");
142 static_assert(WbM != VecN || (AStride % VecN == 0 && MSep % VecN == 0),
143 "vectorised A fragment needs an aligned band base");
144 static_assert(WbN != VecN || (BStride % VecN == 0 && NSep % VecN == 0),
145 "vectorised B fragment needs an aligned band base");
150 const int m =
static_cast<int>(TransA ? A.
cols() : A.
rows());
151 const int k =
static_cast<int>(TransA ? A.
rows() : A.
cols());
152 const int n =
static_cast<int>(TransB ? B.
rows() : B.
cols());
154 const int gm_tiles = (m + TileM - 1) / TileM;
155 const int gn_tiles = (n + TileN - 1) / TileN;
157 const sycl::range<3> local(1, LocalCols, LocalRows);
158 const sycl::range<3> global(
static_cast<size_t>(A.
batch_size()),
159 static_cast<size_t>(gn_tiles) * LocalCols,
160 static_cast<size_t>(gm_tiles) * LocalRows);
162 ctx->submit([&](sycl::handler& h) {
163 sycl::local_accessor<Vec16<D>, 1> tile_a(
164 sycl::range<1>(
static_cast<size_t>(TileK) * AStride / VecN), h);
165 sycl::local_accessor<Vec16<D>, 1> tile_b(
166 sycl::range<1>(
static_cast<size_t>(TileK) * BStride / VecN), h);
168 const D* a_ptr =
reinterpret_cast<const D*
>(A.
data_ptr());
169 const D* b_ptr =
reinterpret_cast<const D*
>(B.
data_ptr());
170 D* c_ptr =
reinterpret_cast<D*
>(C.
data_ptr());
171 const int lda =
static_cast<int>(A.
ld());
172 const int ldb =
static_cast<int>(B.
ld());
173 const int ldc =
static_cast<int>(C.
ld());
174 const int stride_a =
static_cast<int>(A.
stride());
175 const int stride_b =
static_cast<int>(B.
stride());
176 const int stride_c =
static_cast<int>(C.
stride());
177 const int batch =
static_cast<int>(A.
batch_size());
181 __builtin_memcpy(&alpha_d, &alpha,
sizeof(D));
182 __builtin_memcpy(&beta_d, &beta,
sizeof(D));
185 static_cast<int>(P.OpA),
186 static_cast<int>(P.OpB)>>(h,
187 sycl::nd_range<3>(global, local), [=](sycl::nd_item<3> item) {
188 const int bid =
static_cast<int>(item.get_group(0));
195 const int ty =
static_cast<int>(item.get_local_id(2));
196 const int tx =
static_cast<int>(item.get_local_id(1));
197 const int tid = tx * LocalRows + ty;
199 const int m0 =
static_cast<int>(item.get_group(2)) * TileM;
200 const int n0 =
static_cast<int>(item.get_group(1)) * TileN;
202 const D* Ab = a_ptr +
static_cast<std::ptrdiff_t
>(bid) * stride_a;
203 const D* Bb = b_ptr +
static_cast<std::ptrdiff_t
>(bid) * stride_b;
204 D* Cb = c_ptr +
static_cast<std::ptrdiff_t
>(bid) * stride_c;
206 D* sa =
reinterpret_cast<D*
>(
207 tile_a.template get_multi_ptr<sycl::access::decorated::no>().get());
208 D* sb =
reinterpret_cast<D*
>(
209 tile_b.template get_multi_ptr<sycl::access::decorated::no>().get());
213 for (
int i = 0; i < TTM; ++i) {
215 for (
int j = 0; j < TTN; ++j) {
220 for (
int k0 = 0; k0 < k; k0 += TileK) {
228 for (
int idx = tid; idx < TileM * TileK; idx += Threads) {
231 if constexpr (TransA) {
238 const int gm = m0 + i;
239 const int gk = k0 + kk;
241 if (gm < m && gk < k) {
243 ? Ab[gk +
static_cast<std::ptrdiff_t
>(gm) * lda]
244 : Ab[gm +
static_cast<std::ptrdiff_t
>(gk) * lda];
245 if constexpr (ConjA) {
246 value = dev_conj(value);
249 sa[kk * AStride + i] = value;
252 for (
int idx = tid; idx < TileK * TileN; idx += Threads) {
255 if constexpr (TransB) {
262 const int gn = n0 + j;
263 const int gk = k0 + kk;
265 if (gn < n && gk < k) {
267 ? Bb[gn +
static_cast<std::ptrdiff_t
>(gk) * ldb]
268 : Bb[gk +
static_cast<std::ptrdiff_t
>(gn) * ldb];
269 if constexpr (ConjB) {
270 value = dev_conj(value);
273 sb[kk * BStride + j] = value;
275 item.barrier(sycl::access::fence_space::local_space);
280 for (
int kk = 0; kk < TileK; ++kk) {
284 for (
int band = 0; band < NBM; ++band) {
285 load_fragment<WbM, VecN>(
286 &af[band * WbM], &sa[kk * AStride + band * MSep + ty * WbM]);
289 for (
int band = 0; band < NBN; ++band) {
290 load_fragment<WbN, VecN>(
291 &bf[band * WbN], &sb[kk * BStride + band * NSep + tx * WbN]);
294 for (
int i = 0; i < TTM; ++i) {
296 for (
int j = 0; j < TTN; ++j) {
297 fma_acc(accum[i][j], af[i], bf[j]);
301 item.barrier(sycl::access::fence_space::local_space);
309 const bool beta_zero = dev_is_zero(beta_d);
311 for (
int bm = 0; bm < NBM; ++bm) {
313 for (
int w = 0; w < WbM; ++w) {
314 const int row = m0 + bm * MSep + ty * WbM + w;
319 for (
int bn = 0; bn < NBN; ++bn) {
321 for (
int v = 0; v < WbN; ++v) {
322 const int col = n0 + bn * NSep + tx * WbN + v;
326 D* p = &Cb[row +
static_cast<std::ptrdiff_t
>(col) * ldc];
327 const D prior = beta_zero ? D{} : *p;
328 *p = lin_epi(alpha_d, beta_zero ? D{} : beta_d,
329 accum[bm * WbM + w][bn * WbN + v], prior);