Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
cdtp_kernel.h
Go to the documentation of this file.
1#ifndef __MATH_CDTP_KERNEL_H__
2#define __MATH_CDTP_KERNEL_H__
3
4#include "mfma_kernel.h"
5
6/*
7 Copyright (c) 2021-2026, The Neko Authors
8 All rights reserved.
9
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions
12 are met:
13
14 * Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16
17 * Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the following
19 disclaimer in the documentation and/or other materials provided
20 with the distribution.
21
22 * Neither the name of the authors nor the names of its
23 contributors may be used to endorse or promote products derived
24 from this software without specific prior written permission.
25
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
32 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
34 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 POSSIBILITY OF SUCH DAMAGE.
38*/
39
40#include "elem_block.h"
41
46template< typename T, const int LX, const int CHUNKS >
48 const T * __restrict__ x,
49 const T * __restrict__ dr,
50 const T * __restrict__ ds,
51 const T * __restrict__ dt,
52 const T * __restrict__ dxt,
53 const T * __restrict__ dyt,
54 const T * __restrict__ dzt,
55 const T * __restrict__ w3) {
56
60
61 __shared__ T shtar[LX * LX * LX];
62 __shared__ T shtas[LX * LX * LX];
63 __shared__ T shtat[LX * LX * LX];
64
65 const int e = blockIdx.x;
66 const int iii = threadIdx.x;
67 const int nchunks = (LX * LX * LX - 1) / CHUNKS + 1;
68
69 if (iii < (LX * LX)) {
70 shdxt[iii] = dxt[iii];
71 shdyt[iii] = dyt[iii];
72 shdzt[iii] = dzt[iii];
73 }
74
75 int l = iii;
76 while(l < (LX * LX * LX)) {
77 T wx = x[l + e * LX * LX * LX] * w3[l];
78
79 shtar[l] = wx*dr[l + e * LX * LX * LX];
80 shtas[l] = wx*ds[l + e * LX * LX * LX];
81 shtat[l] = wx*dt[l + e * LX * LX * LX];
82
83 l = l + CHUNKS;
84 }
85
87 for (int n = 0; n < nchunks; n++) {
88 const int ijk = iii + n * CHUNKS;
89 const int jk = ijk / LX;
90 const int i = ijk - jk * LX;
91 const int k = jk / LX;
92 const int j = jk - k * LX;
93 if ( i < LX && j < LX && k < LX && ijk < LX*LX*LX) {
94 T rtmp = 0.0;
95 T stmp = 0.0;
96 T ttmp = 0.0;
97 for (int l = 0; l < LX; l++) {
98 rtmp += shdxt[i + l * LX] * shtar[l+j*LX+k*LX*LX];
99 stmp += shdyt[j + l * LX] * shtas[i+l*LX + k*LX*LX];
100 ttmp += shdzt[k + l * LX] * shtat[i + j*LX + l*LX*LX];
101 }
102 dtx[ijk + e * LX * LX * LX] = ( rtmp + stmp + ttmp );
103
104 }
105 }
106}
107
108template< typename T, const int LX, const int EB >
111 const T * __restrict__ x,
119 const int nelv) {
120
124
127
128 static_assert(sizeof(shdxt) +
129 sizeof(shdyt) +
130 sizeof(shdzt) +
131 sizeof(shtar) +
132 sizeof(shtas)
134 "kstep block exceeds the LDS budget");
135
139
140 const int eb = (EB == 1) ? 0 : threadIdx.z;
141 const int e_blk = blockIdx.x * EB + eb;
142 /* Threads past the last element still have to reach the barriers in
143 the k loop, so clamp their reads and drop their stores rather than
144 returning early. At EB == 1 this all constant folds away */
145 const bool active = (EB == 1) ? true : (e_blk < nelv);
146 const int e = active ? e_blk : (nelv - 1);
147 const int sh = eb * LX * LX;
148 const int j = threadIdx.y;
149 const int i = threadIdx.x;
150 const int ij = i + j * LX;
151 const int ele = e*LX*LX*LX;
152
153 if (eb == 0) {
154 shdxt[ij] = dxt[ij];
155 shdyt[ij] = dyt[ij];
156 shdzt[ij] = dzt[ij];
157 }
158
159
160#pragma unroll LX
161 for (int k = 0; k < LX; ++k) {
162 T wx = x[ij + k*LX*LX + ele] * w3[ij + k*LX*LX];
163
164 rtar[k] = wx *dr[ij + k*LX*LX + ele];
165 rtas[k] = wx *ds[ij + k*LX*LX + ele];
166 rtat[k] = wx *dt[ij + k*LX*LX + ele];
167 }
168
170
171#pragma unroll
172 for (int k = 0; k < LX; ++k) {
173 const int ijk = ij + k*LX*LX;
174 T ttmp = 0.0;
175 shtar[sh + ij] = rtar[k];
176 shtas[sh + ij] = rtas[k];
177#pragma unroll
178 for (int l = 0; l < LX; l++) {
179 ttmp += shdzt[k+l*LX] * rtat[l];
180 }
182
183 T rtmp = 0.0;
184 T stmp = 0.0;
185#pragma unroll
186 for (int l = 0; l < LX; l++) {
187 rtmp += shdxt[i+l*LX] * shtar[sh + l+j*LX];
188 stmp += shdyt[j+l*LX] * shtas[sh + i+l*LX];
189 }
190
191 if (active) {
192 dtx[ijk + ele] = ( rtmp + stmp + ttmp );
193 }
194
196 }
197}
198
199
200
217#if defined(__gfx90a__) || defined(__gfx942__)
218
219template< typename T, const int LX, const int NWF >
221 const T * __restrict__ x,
222 const T * __restrict__ dr,
223 const T * __restrict__ ds,
224 const T * __restrict__ dt,
225 const T * __restrict__ dxt,
226 const T * __restrict__ dyt,
227 const T * __restrict__ dzt,
228 const T * __restrict__ w3,
229 const int nelv) {
230 const int LX2 = LX * LX;
231 const int LX3 = LX * LX * LX;
232
233 /* NWF wavefronts per block, WPE of them cooperating on one element and the
234 block covering EB elements, see the note in mfma_kernel.h. At LX = 4 the
235 contraction offers one column group, so WPE is 1 and every wavefront gets
236 an element of its own rather than idling. */
237 enum { EB = NEKO_MFMA_EB_N(NWF, LX),
238 WPE = NWF / EB };
239 static_assert(WPE * EB == NWF,
240 "wavefronts per block must split evenly over the elements");
241
245 __shared__ T shtar[EB * LX * LX * LX]; // the three weighted fields
246 __shared__ T shtas[EB * LX * LX * LX];
247 __shared__ T shtat[EB * LX * LX * LX];
248 __shared__ T shout[EB * LX * LX * LX]; // the accumulated result
249
250 static_assert(sizeof(shdxt) + sizeof(shdyt) + sizeof(shdzt) +
251 sizeof(shtar) + sizeof(shtas) + sizeof(shtat) +
252 sizeof(shout)
254 "mfma block exceeds the shared memory budget");
255
256 const int lane = threadIdx.x; // 0..63 : lane within a wavefront
257 const int wf = threadIdx.y; // 0..NWF-1 : which wavefront
258 const int tid = wf * 64 + lane; // 0..NWF*64-1 : block-wide thread id
259 const int nthr = NWF * 64;
260
261 const int eb = wf / WPE; // which element this wavefront serves
262 const int sub = wf % WPE; // its rank among that element's waves
263 const int gtid = sub * 64 + lane; // thread id within the element group
264 const int gnthr = WPE * 64;
265
266 /* Threads past the last element still have to reach the block wide
267 barriers, so clamp their reads and drop their stores rather than
268 returning early. At EB == 1 the grid covers nelv exactly and this is
269 constant folded away */
270 const int e_blk = blockIdx.x * EB + eb;
271 const bool active = (EB == 1) ? true : (e_blk < nelv);
272 const int e = active ? e_blk : (nelv - 1);
273 const int ele = e * LX3;
274 const int sh = eb * LX3;
275
276 /* Reference derivative matrices, already transposed, one copy per block */
277 for (int p = tid; p < LX2; p += nthr) {
278 shdxt[p] = dxt[p];
279 shdyt[p] = dyt[p];
280 shdzt[p] = dzt[p];
281 }
282 /* The three weighted fields, formed before anything is contracted, and the
283 accumulator cleared alongside them */
284 for (int p = gtid; p < LX3; p += gnthr) {
285 const int gp = p + ele;
286 const T wx = x[gp] * w3[p];
287
288 shtar[sh + p] = wx * dr[gp];
289 shtas[sh + p] = wx * ds[gp];
290 shtat[sh + p] = wx * dt[gp];
291 shout[sh + p] = 0.0;
292 }
293
295
296 /* dtx = Dr^T tar + Ds^T tas + Dt^T tat, accumulated in shout */
297 mfma_contract_sel<T, LX, 0, false, true, WPE>::run(shout + sh, shdxt,
298 shtar + sh, lane, sub);
300 mfma_contract_sel<T, LX, 1, false, true, WPE>::run(shout + sh, shdyt,
301 shtas + sh, lane, sub);
303 mfma_contract_sel<T, LX, 2, false, true, WPE>::run(shout + sh, shdzt,
304 shtat + sh, lane, sub);
306
307 if (active) {
308 for (int p = gtid; p < LX3; p += gnthr)
309 dtx[p + ele] = shout[sh + p];
310 }
311}
312
313#endif // __gfx90a__ || __gfx942__
314
315/*
316 * Compile-time dispatch onto the MFMA element kernel. The launch macros in
317 * opr_cdtp.hip are written for every LX the operator dispatches and for
318 * whatever `real` is, so every combination has to compile; the ones the
319 * strategy does not cover -- LX outside the supported range, a build without
320 * a matrix-core arch -- resolve to this no-op. The autotuner never selects
321 * the strategy for them, so the no-op is unreachable at runtime, see
322 * mfma_lx_supported() and hip_have_mfma() in mfma_kernel.h.
323 */
324template< typename T, const int LX, const int NWF >
326 __device__ static void run(T *, const T *, const T *, const T *, const T *,
327 const T *, const T *, const T *, const T *,
328 const int) {}
329};
330
331#if defined(__gfx90a__) || defined(__gfx942__)
332
333/* Keep in sync with mfma_lx_supported() in mfma_kernel.h */
334#define NEKO_CDTP_MFMA_DISPATCH(TYPE, LXV) \
335 template< const int NWF > \
336 struct cdtp_mfma_dispatch< TYPE, LXV, NWF > { \
337 __device__ static void run(TYPE * dtx, \
338 const TYPE * x, \
339 const TYPE * dr, \
340 const TYPE * ds, \
341 const TYPE * dt, \
342 const TYPE * dxt, \
343 const TYPE * dyt, \
344 const TYPE * dzt, \
345 const TYPE * w3, \
346 const int nelv) { \
347 cdtp_mfma_elem< TYPE, LXV, NWF >(dtx, x, dr, ds, dt, dxt, dyt, dzt, \
348 w3, nelv); \
349 } \
350 }
351
352NEKO_CDTP_MFMA_DISPATCH(double, 4);
353NEKO_CDTP_MFMA_DISPATCH(double, 5);
354NEKO_CDTP_MFMA_DISPATCH(double, 6);
355NEKO_CDTP_MFMA_DISPATCH(double, 7);
356NEKO_CDTP_MFMA_DISPATCH(double, 8);
357NEKO_CDTP_MFMA_DISPATCH(double, 9);
358NEKO_CDTP_MFMA_DISPATCH(double, 10);
359NEKO_CDTP_MFMA_DISPATCH(double, 11);
360NEKO_CDTP_MFMA_DISPATCH(double, 12);
361
368NEKO_CDTP_MFMA_DISPATCH(float, 10);
369NEKO_CDTP_MFMA_DISPATCH(float, 11);
370NEKO_CDTP_MFMA_DISPATCH(float, 12);
371
372#endif // __gfx90a__ || __gfx942__
373
374/*
375 * Note the bare __launch_bounds__ rather than NEKO_EB_BOUNDS, matching
376 * ax_helm_kernel_mfma: the kstep kernels ask for three waves per SIMD, and
377 * the matrix core kernels were validated without that constraint.
378 */
379template< typename T, const int LX, const int NWF >
382 const T * __restrict__ x,
383 const T * __restrict__ dr,
384 const T * __restrict__ ds,
385 const T * __restrict__ dt,
386 const T * __restrict__ dxt,
387 const T * __restrict__ dyt,
388 const T * __restrict__ dzt,
389 const T * __restrict__ w3,
390 const int nelv) {
391
393 w3, nelv);
394}
395
396#endif // __MATH_CDTP_KERNEL_H__
__global__ void ale_add_kinematics_kernel(const int n, T *__restrict__ wx, T *__restrict__ wy, T *__restrict__ wz, const T *__restrict__ x_ref, const T *__restrict__ y_ref, const T *__restrict__ z_ref, const T *__restrict__ phi, const T *__restrict__ x, const T *__restrict__ y, const T *__restrict__ z, const kinematics_params_t kin_params)
const bool active
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dyt
T rtas[LX]
const int sh
const int eb
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ ds
__shared__ T shdzt[LX *LX]
const int i
T rtat[LX]
const int ij
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dzt
const int e
__global__ void const T *__restrict__ x
T rtar[LX]
__global__ void const T *__restrict__ const T *__restrict__ dr
const int e_blk
const int ele
__shared__ T shtar[EB *LX *LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const int nelv
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dt
const int j
__shared__ T shtas[EB *LX *LX]
__global__ void cdtp_kernel_1d(T *__restrict__ dtx, const T *__restrict__ x, const T *__restrict__ dr, const T *__restrict__ ds, const T *__restrict__ dt, const T *__restrict__ dxt, const T *__restrict__ dyt, const T *__restrict__ dzt, const T *__restrict__ w3)
Definition cdtp_kernel.h:46
__shared__ T shdyt[LX *LX]
__syncthreads()
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dxt
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ w3
#define NEKO_EB_BOUNDS(NT)
Definition elem_block.h:95
__global__ void __launch_bounds__((LX *LX *EB), 3) cdtp_kernel_kstep(T *__restrict__ dtx
#define NEKO_EB_MAX_LDS
Definition elem_block.h:79
#define NEKO_MFMA_EB_N(NWF, LX)
static __device__ void run(T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const int)