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 "elem_block.h"
5#include "dmma_kernel.h"
6#include "dmma_tma_kernel.h"
7/*
8 Copyright (c) 2021-2026, The Neko Authors
9 All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14
15 * Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
17
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the following
20 disclaimer in the documentation and/or other materials provided
21 with the distribution.
22
23 * Neither the name of the authors nor the names of its
24 contributors may be used to endorse or promote products derived
25 from this software without specific prior written permission.
26
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
30 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
31 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
32 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
33 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
35 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 POSSIBILITY OF SUCH DAMAGE.
39*/
40
45template< typename T, const int LX, const int CHUNKS >
47 const T * __restrict__ x,
48 const T * __restrict__ dr,
49 const T * __restrict__ ds,
50 const T * __restrict__ dt,
51 const T * __restrict__ dxt,
52 const T * __restrict__ dyt,
53 const T * __restrict__ dzt,
54 const T * __restrict__ w3) {
55
59
60 __shared__ T shtar[LX * LX * LX];
61 __shared__ T shtas[LX * LX * LX];
62 __shared__ T shtat[LX * LX * LX];
63
64 const int e = blockIdx.x;
65 const int iii = threadIdx.x;
66 const int nchunks = (LX * LX * LX - 1) / CHUNKS + 1;
67
68 if (iii < (LX * LX)) {
69 shdxt[iii] = dxt[iii];
70 shdyt[iii] = dyt[iii];
71 shdzt[iii] = dzt[iii];
72 }
73
74 int l = iii;
75 while(l < (LX * LX * LX)) {
76 T wx = x[l + e * LX * LX * LX] * w3[l];
77
78 shtar[l] = wx*dr[l + e * LX * LX * LX];
79 shtas[l] = wx*ds[l + e * LX * LX * LX];
80 shtat[l] = wx*dt[l + e * LX * LX * LX];
81
82 l = l + CHUNKS;
83 }
84
86 for (int n = 0; n < nchunks; n++) {
87 const int ijk = iii + n * CHUNKS;
88 const int jk = ijk / LX;
89 const int i = ijk - jk * LX;
90 const int k = jk / LX;
91 const int j = jk - k * LX;
92 if ( i < LX && j < LX && k < LX && ijk < LX*LX*LX) {
93 T rtmp = 0.0;
94 T stmp = 0.0;
95 T ttmp = 0.0;
96 for (int l = 0; l < LX; l++) {
97 rtmp += shdxt[i + l * LX] * shtar[l+j*LX+k*LX*LX];
98 stmp += shdyt[j + l * LX] * shtas[i+l*LX + k*LX*LX];
99 ttmp += shdzt[k + l * LX] * shtat[i + j*LX + l*LX*LX];
100 }
101 dtx[ijk + e * LX * LX * LX] = ( rtmp + stmp + ttmp );
102
103 }
104 }
105}
106
107template< typename T, const int LX, const int EB >
110 const T * __restrict__ x,
118 const int nelv) {
119
123
126
127 static_assert(sizeof(shdxt) +
128 sizeof(shdyt) +
129 sizeof(shdzt) +
130 sizeof(shtar) +
131 sizeof(shtas)
133 "kstep block exceeds the shared memory budget");
134
138
139 const int eb = (EB == 1) ? 0 : threadIdx.z;
140 const int e_blk = blockIdx.x * EB + eb;
141 /* Threads past the last element still have to reach the barriers in
142 the k loop, so clamp their reads and drop their stores rather than
143 returning early. At EB == 1 this all constant folds away */
144 const bool active = (EB == 1) ? true : (e_blk < nelv);
145 const int e = active ? e_blk : (nelv - 1);
146 const int sh = eb * LX * LX;
147 const int j = threadIdx.y;
148 const int i = threadIdx.x;
149 const int ij = i + j * LX;
150 const int ele = e*LX*LX*LX;
151
152 if (eb == 0) {
153 shdxt[ij] = dxt[ij];
154 shdyt[ij] = dyt[ij];
155 shdzt[ij] = dzt[ij];
156 }
157
158
159#pragma unroll LX
160 for (int k = 0; k < LX; ++k) {
161 T wx = x[ij + k*LX*LX + ele] * w3[ij + k*LX*LX];
162
163 rtar[k] = wx *dr[ij + k*LX*LX + ele];
164 rtas[k] = wx *ds[ij + k*LX*LX + ele];
165 rtat[k] = wx *dt[ij + k*LX*LX + ele];
166 }
167
169
170#pragma unroll
171 for (int k = 0; k < LX; ++k) {
172 const int ijk = ij + k*LX*LX;
173 T ttmp = 0.0;
174 shtar[sh + ij] = rtar[k];
175 shtas[sh + ij] = rtas[k];
176#pragma unroll
177 for (int l = 0; l < LX; l++) {
178 ttmp += shdzt[k+l*LX] * rtat[l];
179 }
181
182 T rtmp = 0.0;
183 T stmp = 0.0;
184#pragma unroll
185 for (int l = 0; l < LX; l++) {
186 rtmp += shdxt[i+l*LX] * shtar[sh + l+j*LX];
187 stmp += shdyt[j+l*LX] * shtas[sh + i+l*LX];
188 }
189
190 if (active) {
191 dtx[ijk + ele] = ( rtmp + stmp + ttmp );
192 }
193
195 }
196}
197
198
199
229#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 800) && (__CUDA_ARCH__ < 1000)
230
231template< const int LX, const int NW >
233void cdtp_dmma_elem(double * __restrict__ dtx,
234 const double * __restrict__ x,
235 const double * __restrict__ dr,
236 const double * __restrict__ ds,
237 const double * __restrict__ dt,
238 const double * __restrict__ dxt,
239 const double * __restrict__ dyt,
240 const double * __restrict__ dzt,
241 const double * __restrict__ w3,
242 const int nelv) {
243
244 /* Element independent, one copy per block. Block diagonal when more than
245 one element is packed: one LX x LX copy of D^T per sub-cube */
249
250 /* The pack, padded to DMMA_P^3: the three weighted fields and the
251 accumulated result */
256
258 sizeof(shdyt) +
259 sizeof(shdzt) +
260 sizeof(shtar) +
261 sizeof(shtas) +
262 sizeof(shtat) +
266
267 /* Elements per sub-cube axis, and per cube, see the note in dmma_kernel.h */
268 enum { PPA = (DMMA_P % LX == 0) ? (DMMA_P / LX) : 1,
269 PACK = PPA * PPA * PPA,
270 LX3 = LX * LX * LX,
271 NP = PACK * LX3 };
272
274
275 const int nthrds = 32 * NW;
276 const int tid = threadIdx.x;
277 const int wf = tid >> 5;
278 const int ebase = pack::ebase();
279
280 /* The padding has to be finite, see the note in dmma_kernel.h. All three
281 inputs are contracted, so all three are zero filled -- unlike the phase 1
282 kernels, which stage a single field. shout needs none: the axis 0
283 contraction initialises every tile of it */
284 if (PACK * LX3 < DMMA_CUBE) {
285 for (int p = tid; p < DMMA_CUBE; p += nthrds) {
286 shtar[p] = 0.0;
287 shtas[p] = 0.0;
288 shtat[p] = 0.0;
289 }
290 }
291 if (LX < DMMA_P) {
292 for (int p = tid; p < DMMA_MAT; p += nthrds) {
293 shdxt[p] = 0.0;
294 shdyt[p] = 0.0;
295 shdzt[p] = 0.0;
296 }
297 }
298 if (LX < DMMA_P) {
300 }
301
302 /* One copy of D^T per sub-cube, down the diagonal */
303 for (int p = tid; p < LX * LX; p += nthrds) {
304 const int i = p % LX;
305 const int l = p / LX;
306#pragma unroll
307 for (int b = 0; b < PPA; b++) {
308 const int m = (b * LX + i) + DMMA_P * (b * LX + l);
309 shdxt[m] = dxt[p];
310 shdyt[m] = dyt[p];
311 shdzt[m] = dzt[p];
312 }
313 }
314
315 for (int p = tid; p < NP; p += nthrds) {
316 const dmma_idx idx = pack::map(p, ebase, nelv);
317 const int c = idx.c;
318 const int gp = idx.g;
319 const double wx = x[gp] * w3[idx.l];
320
321 shtar[c] = wx * dr[gp];
322 shtas[c] = wx * ds[gp];
323 shtat[c] = wx * dt[gp];
324 }
325
327
328 /* Axis 0 initialises the output, the other two add into it. The barriers
329 are needed because the j slab tiles of axis 2 span what every warp wrote
330 along axes 0 and 1; see the same sequence in ax_helm_dmma_elem() */
337
338 for (int p = tid; p < NP; p += nthrds) {
339 const dmma_idx idx = pack::map(p, ebase, nelv);
340
341 if (idx.live) {
342 dtx[idx.g] = shout[idx.c];
343 }
344 }
345}
346
347#endif // __CUDA_ARCH__ in [800, 1000)
348
349/*
350 * Compile-time dispatch onto the DMMA element kernel. The launch macros in
351 * opr_cdtp.cu are written for every LX the operator dispatches and for
352 * whatever `real` is, so every combination has to compile; the ones the
353 * strategy does not cover -- single precision, LX outside the supported
354 * range, a build without an fp64 tensor core arch -- resolve to this no-op.
355 * The autotuner never selects the strategy for them, see dmma_lx_supported()
356 * and cuda_have_dmma() in dmma_kernel.h.
357 */
358template< typename T, const int LX, const int NW >
361 const T * __restrict__, const T * __restrict__,
362 const T * __restrict__, const T * __restrict__,
363 const T * __restrict__, const T * __restrict__,
364 const T * __restrict__, const T * __restrict__,
365 const int) { }
366};
367
368#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 800) && (__CUDA_ARCH__ < 1000)
369
370/* Keep in sync with dmma_lx_supported() in dmma_kernel.h */
371#define NEKO_CDTP_DMMA_DISPATCH(LXV) \
372 template< const int NW > \
373 struct cdtp_dmma_dispatch< double, LXV, NW > { \
374 __device__ static void run(double * __restrict__ dtx, \
375 const double * __restrict__ x, \
376 const double * __restrict__ dr, \
377 const double * __restrict__ ds, \
378 const double * __restrict__ dt, \
379 const double * __restrict__ dxt, \
380 const double * __restrict__ dyt, \
381 const double * __restrict__ dzt, \
382 const double * __restrict__ w3, \
383 const int nelv) { \
384 cdtp_dmma_elem< LXV, NW >(dtx, x, dr, ds, dt, \
385 dxt, dyt, dzt, w3, nelv); \
386 } \
387 }
388
396
397#endif // __CUDA_ARCH__ in [800, 1000)
398
399template< typename T, const int LX, const int NW >
402 const T * __restrict__ x,
403 const T * __restrict__ dr,
404 const T * __restrict__ ds,
405 const T * __restrict__ dt,
406 const T * __restrict__ dxt,
407 const T * __restrict__ dyt,
408 const T * __restrict__ dzt,
409 const T * __restrict__ w3,
410 const int nelv) {
411
413 dxt, dyt, dzt, w3, nelv);
414}
415
445#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) && \
446 (__CUDA_ARCH__ < 1000) && NEKO_TMA_TOOLKIT
447
448template< const int LX, const int NW >
451 const double * __restrict__ x,
452 const double * __restrict__ dr,
453 const double * __restrict__ ds,
454 const double * __restrict__ dt,
455 const double * __restrict__ dxt,
456 const double * __restrict__ dyt,
457 const double * __restrict__ dzt,
458 const double * __restrict__ w3) {
459
460 /* A bulk copy needs 16 byte alignment at both ends; the cubes are given the
461 128 the tensor variants would want anyway */
463 __shared__ __align__(128) double shdyt[DMMA_MAT];
464 __shared__ __align__(128) double shdzt[DMMA_MAT];
465
466 /* x, and the three factors which become the weighted fields in place */
467 __shared__ __align__(128) double shx[DMMA_CUBE];
468 __shared__ __align__(128) double shtar[DMMA_CUBE];
469 __shared__ __align__(128) double shtas[DMMA_CUBE];
470 __shared__ __align__(128) double shtat[DMMA_CUBE];
471 __shared__ __align__(128) double shout[DMMA_CUBE];
472
473 __shared__ __align__(8) unsigned long long bar_a;
474 __shared__ __align__(8) unsigned long long bar_b;
475
477 sizeof(shdyt) +
478 sizeof(shdzt) +
479 sizeof(shx) +
480 sizeof(shtar) +
481 sizeof(shtas) +
482 sizeof(shtat) +
483 sizeof(shout) +
484 sizeof(bar_a) +
485 sizeof(bar_b)
488
489 /* Only lx == DMMA_P stages as a contiguous run of bytes, which is what a
490 bulk copy moves; see the scope note in dmma_tma_kernel.h. Keep in step
491 with dmma_tma_cdtp_lx_supported() */
494
495 enum { CUBE_BYTES = DMMA_CUBE * (int) sizeof(double) };
496
497 const int nthrds = 32 * NW;
498 const int tid = threadIdx.x;
499 const int wf = tid >> 5;
500 const int ebase = blockIdx.x * DMMA_CUBE;
501
502 if (tid == 0) {
503 tma_barrier_init(&bar_a, 1);
504 tma_barrier_init(&bar_b, 1);
505 }
506
507 /* At LX == DMMA_P the derivative matrix fills the staged one exactly, and
508 there is no padding anywhere to zero */
509 for (int p = tid; p < DMMA_MAT; p += nthrds) {
510 shdxt[p] = dxt[p];
511 shdyt[p] = dyt[p];
512 shdzt[p] = dzt[p];
513 }
514
515 /* Both barriers initialised and every derivative matrix staged before
516 anyone waits on the one or contracts with the other */
518
519 /* All four copies issued together; only the first two are waited on now */
520 if (tid == 0) {
521 tma_expect(&bar_a, 2 * CUBE_BYTES);
522 tma_load(shx, x + ebase, CUBE_BYTES, &bar_a);
523 tma_load(shtar, dr + ebase, CUBE_BYTES, &bar_a);
524
525 tma_expect(&bar_b, 2 * CUBE_BYTES);
526 tma_load(shtas, ds + ebase, CUBE_BYTES, &bar_b);
527 tma_load(shtat, dt + ebase, CUBE_BYTES, &bar_b);
528 }
529
530 tma_wait(&bar_a, 0);
531
532 /* At LX == DMMA_P the cube offset, the point within the element and the
533 loop counter are all the same index, so w3 is read at p. The weighted
534 field overwrites the staged factor it was formed from */
535 for (int p = tid; p < DMMA_CUBE; p += nthrds) {
536 shtar[p] *= shx[p] * w3[p];
537 }
538
540
542
543 /* ds and dt have been in flight underneath that contraction */
544 tma_wait(&bar_b, 0);
545
546 for (int p = tid; p < DMMA_CUBE; p += nthrds) {
547 const double wx = shx[p] * w3[p];
548
549 shtas[p] *= wx;
550 shtat[p] *= wx;
551 }
552
554
558
559 /* The contractions wrote shout through the generic proxy and the bulk store
560 reads it through the async one, so the fence is needed on top of the
561 barrier; see tma_fence_shared() */
564
565 if (tid == 0) {
568 }
569
570 /* The store reads shout asynchronously, and shared memory lives only as long
571 as the block does: nothing may retire until it has been read out */
573}
574
575#endif // __CUDA_ARCH__ == sm_90 with a CUDA 12 toolkit
576
577/*
578 * Compile-time dispatch onto the TMA staged DMMA element kernel, see the note
579 * on cdtp_dmma_dispatch above. The no-op covers everything the strategy does
580 * not: single precision, any lx but DMMA_P, a build without sm_90, and a
581 * toolkit older than CUDA 12.
582 */
583template< typename T, const int LX, const int NW >
586 const T * __restrict__, const T * __restrict__,
587 const T * __restrict__, const T * __restrict__,
588 const T * __restrict__, const T * __restrict__,
589 const T * __restrict__, const T * __restrict__) { }
590};
591
592#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) && \
593 (__CUDA_ARCH__ < 1000) && NEKO_TMA_TOOLKIT
594
595/* Keep in sync with dmma_tma_cdtp_lx_supported() in dmma_tma_kernel.h */
596#define NEKO_CDTP_DMMA_TMA_DISPATCH(LXV) \
597 template< const int NW > \
598 struct cdtp_dmma_tma_dispatch< double, LXV, NW > { \
599 __device__ static void run(double * __restrict__ dtx, \
600 const double * __restrict__ x, \
601 const double * __restrict__ dr, \
602 const double * __restrict__ ds, \
603 const double * __restrict__ dt, \
604 const double * __restrict__ dxt, \
605 const double * __restrict__ dyt, \
606 const double * __restrict__ dzt, \
607 const double * __restrict__ w3) { \
608 cdtp_dmma_tma_elem< LXV, NW >(dtx, x, dr, ds, dt, \
609 dxt, dyt, dzt, w3); \
610 } \
611 }
612
614
615#endif // __CUDA_ARCH__ == sm_90 with a CUDA 12 toolkit
616
617template< typename T, const int LX, const int NW >
620 const T * __restrict__ x,
621 const T * __restrict__ dr,
622 const T * __restrict__ ds,
623 const T * __restrict__ dt,
624 const T * __restrict__ dxt,
625 const T * __restrict__ dyt,
626 const T * __restrict__ dzt,
627 const T * __restrict__ w3) {
628
630 dxt, dyt, dzt, w3);
631}
632
633#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_MAX_SMEM
Definition elem_block.h:60
#define NEKO_EB_BOUNDS(NT)
Definition elem_block.h:95
@ DMMA_CUBE
@ DMMA_MAT
@ DMMA_P
static __device__ void run(T *__restrict__, 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)
static __device__ void run(T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__)