Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
dudxyz_kernel.h
Go to the documentation of this file.
1#ifndef __MATH_DUDXYZ_KERNEL_H__
2#define __MATH_DUDXYZ_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__ u,
48 const T * __restrict__ dr,
49 const T * __restrict__ ds,
50 const T * __restrict__ dt,
51 const T * __restrict__ dx,
52 const T * __restrict__ dy,
53 const T * __restrict__ dz,
54 const T * __restrict__ jacinv) {
55
56 __shared__ T shu[LX * LX * LX];
57 __shared__ T shdr[LX * LX * LX];
58 __shared__ T shds[LX * LX * LX];
59 __shared__ T shdt[LX * LX * LX];
60
64
66
67 const int e = blockIdx.x;
68 const int iii = threadIdx.x;
69 const int nchunks = (LX * LX * LX - 1) / CHUNKS + 1;
70
71 if (iii < (LX * LX)) {
72 shdx[iii] = dx[iii];
73 shdy[iii] = dy[iii];
74 shdz[iii] = dz[iii];
75 }
76
77 int l = iii;
78 while(l < (LX * LX * LX)) {
79 shu[l] = u[l + e * LX * LX * LX];
80 shdr[l] = dr[l + e * LX * LX * LX];
81 shds[l] = ds[l + e * LX * LX * LX];
82 shdt[l] = dt[l + e * LX * LX * LX];
83 shjacinv[l] = jacinv[l + e * LX * LX * LX];
84 l = l + CHUNKS;
85 }
86
88
89 for (int n = 0; n < nchunks; n++) {
90 const int ijk = iii + n * CHUNKS;
91 const int jk = ijk / LX;
92 const int i = ijk - jk * LX;
93 const int k = jk / LX;
94 const int j = jk - k * LX;
95 if ( i < LX && j < LX && k < LX) {
96 T rtmp = 0.0;
97 T stmp = 0.0;
98 T ttmp = 0.0;
99 for (int l = 0; l < LX; l++) {
100 rtmp += shdx[i + l * LX] * shu[l + j * LX + k * LX * LX];
101 stmp += shdy[j + l * LX] * shu[i + l * LX + k * LX * LX];
102 ttmp += shdz[k + l * LX] * shu[i + j * LX + l * LX * LX];
103 }
104 du[ijk + e * LX * LX * LX] = ((rtmp * shdr[ijk])
105 + (stmp * shds[ijk])
106 + (ttmp * shdt[ijk]))
107 * shjacinv[ijk];
108
109 }
110 }
111}
112
113template< typename T, const int LX, const int EB >
116 const T * __restrict__ u,
124 const int nelv) {
125
126 __shared__ T shu[EB * LX * LX];
127
131
132 static_assert(sizeof(shu) +
133 sizeof(shdx) +
134 sizeof(shdy) +
135 sizeof(shdz)
137 "kstep block exceeds the shared memory budget");
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 shdx[ij] = dx[ij];
154 shdy[ij] = dy[ij];
155 shdz[ij] = dz[ij];
156 }
157
163
164 #pragma unroll LX
165 for (int k = 0; k < LX; ++k) {
166 ru[k] = u[ij + k*LX*LX + ele];
167 rdr[k] = dr[ij + k*LX*LX + ele];
168 rds[k] = ds[ij + k*LX*LX + ele];
169 rdt[k] = dt[ij + k*LX*LX + ele];
170 rjacinv[k] = jacinv[ij + k*LX*LX + ele];
171 }
172
174
175 #pragma unroll
176 for (int k = 0; k < LX; ++k) {
177 const int ijk = ij + k*LX*LX;
178 T ttmp = 0.0;
179 shu[sh + ij] = ru[k];
180#pragma unroll
181 for (int l = 0; l < LX; l++) {
182 ttmp += shdz[k+l*LX] * ru[l];
183 }
185
186 T rtmp = 0.0;
187 T stmp = 0.0;
188#pragma unroll
189 for (int l = 0; l < LX; l++) {
190 rtmp += shdx[i+l*LX] * shu[sh + l+j*LX];
191 stmp += shdy[j+l*LX] * shu[sh + i+l*LX];
192 }
193
194 if (active) {
195 du[ijk + ele] = rjacinv[k] * ((rtmp * rdr[k])
196 + (stmp * rds[k])
197 + (ttmp * rdt[k]));
198 }
200 }
201}
202
203
226#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 800) && (__CUDA_ARCH__ < 1000)
227
228template< const int LX, const int NW >
230void dudxyz_dmma_elem(double * __restrict__ du,
231 const double * __restrict__ u,
232 const double * __restrict__ dr,
233 const double * __restrict__ ds,
234 const double * __restrict__ dt,
235 const double * __restrict__ dx,
236 const double * __restrict__ dy,
237 const double * __restrict__ dz,
238 const double * __restrict__ jacinv,
239 const int nelv) {
240
241 /* Element independent, one copy per block. Block diagonal when more than
242 one element is packed: one LX x LX copy of D per sub-cube */
244 __shared__ __align__(16) double shdy[DMMA_MAT];
245 __shared__ __align__(16) double shdz[DMMA_MAT];
246
247 /* The pack, padded to DMMA_P^3. shu carries the input, shr, shs and sht
248 the reference derivatives */
249 __shared__ __align__(16) double shu[DMMA_CUBE];
250 __shared__ __align__(16) double shr[DMMA_CUBE];
251 __shared__ __align__(16) double shs[DMMA_CUBE];
252 __shared__ __align__(16) double sht[DMMA_CUBE];
253
255 sizeof(shdy) +
256 sizeof(shdz) +
257 sizeof(shu) +
258 sizeof(shr) +
259 sizeof(shs) +
260 sizeof(sht)
263
264 /* Elements per sub-cube axis, and per cube, see the note in dmma_kernel.h.
265 At PPA == 1 the addressing is not left to constant fold -- dmma_pack is
266 specialised so the tail clamp and the guarded store are never emitted at
267 all, see the note there */
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. At
281 LX == DMMA_P with one element packed there is none and this is folded
282 away */
283 if (PACK * LX3 < DMMA_CUBE) {
284 for (int p = tid; p < DMMA_CUBE; p += nthrds) {
285 shu[p] = 0.0;
286 }
287 }
288 if (LX < DMMA_P) {
289 for (int p = tid; p < DMMA_MAT; p += nthrds) {
290 shdx[p] = 0.0;
291 shdy[p] = 0.0;
292 shdz[p] = 0.0;
293 }
294 }
295 if (LX < DMMA_P) {
297 }
298
299 /* One copy of D per sub-cube, down the diagonal */
300 for (int p = tid; p < LX * LX; p += nthrds) {
301 const int i = p % LX;
302 const int l = p / LX;
303#pragma unroll
304 for (int b = 0; b < PPA; b++) {
305 const int m = (b * LX + i) + DMMA_P * (b * LX + l);
306 shdx[m] = dx[p];
307 shdy[m] = dy[p];
308 shdz[m] = dz[p];
309 }
310 }
311
312 for (int p = tid; p < NP; p += nthrds) {
313 const dmma_idx x = pack::map(p, ebase, nelv);
314
315 shu[x.c] = u[x.g];
316 }
317
319
323
325
326 /* No second set of contractions and so no restaging: the metrics are
327 applied on the way out. A dead tail slot reads nothing at all here,
328 unlike the axhelm kernel where the clamped read still has a shared write
329 to feed; at PACK == 1 the guard is a compile time true and folds */
330 for (int p = tid; p < NP; p += nthrds) {
331 const dmma_idx x = pack::map(p, ebase, nelv);
332
333 if (x.live) {
334 const int c = x.c;
335 const int gp = x.g;
336
337 du[gp] = jacinv[gp] * ((shr[c] * dr[gp])
338 + (shs[c] * ds[gp])
339 + (sht[c] * dt[gp]));
340 }
341 }
342}
343
344#endif // __CUDA_ARCH__ in [800, 1000)
345
346/*
347 * Compile-time dispatch onto the DMMA element kernel. The launch macros in
348 * opr_dudxyz.cu are written for every LX the operator dispatches and for
349 * whatever `real` is, so every combination has to compile; the ones the
350 * strategy does not cover -- single precision, LX outside the supported
351 * range, a build without an fp64 tensor core arch -- resolve to this no-op.
352 * The autotuner never selects the strategy for them, so the no-op is
353 * unreachable at runtime, see dmma_lx_supported() and cuda_have_dmma() in
354 * dmma_kernel.h.
355 */
356template< typename T, const int LX, const int NW >
359 const T * __restrict__,
360 const T * __restrict__,
361 const T * __restrict__,
362 const T * __restrict__,
363 const T * __restrict__,
364 const T * __restrict__,
365 const T * __restrict__,
366 const T * __restrict__,
367 const int) { }
368};
369
370#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 800) && (__CUDA_ARCH__ < 1000)
371
372/* Keep in sync with dmma_lx_supported() in dmma_kernel.h */
373#define NEKO_DUDXYZ_DMMA_DISPATCH(LXV) \
374 template< const int NW > \
375 struct dudxyz_dmma_dispatch< double, LXV, NW > { \
376 __device__ static void run(double * __restrict__ du, \
377 const double * __restrict__ u, \
378 const double * __restrict__ dr, \
379 const double * __restrict__ ds, \
380 const double * __restrict__ dt, \
381 const double * __restrict__ dx, \
382 const double * __restrict__ dy, \
383 const double * __restrict__ dz, \
384 const double * __restrict__ jacinv, \
385 const int nelv) { \
386 dudxyz_dmma_elem< LXV, NW >(du, u, dr, ds, dt, \
387 dx, dy, dz, jacinv, nelv); \
388 } \
389 }
390
398
399#endif // __CUDA_ARCH__ in [800, 1000)
400
401template< typename T, const int LX, const int NW >
404 const T * __restrict__ u,
405 const T * __restrict__ dr,
406 const T * __restrict__ ds,
407 const T * __restrict__ dt,
408 const T * __restrict__ dx,
409 const T * __restrict__ dy,
410 const T * __restrict__ dz,
411 const T * __restrict__ jacinv,
412 const int nelv) {
413
415 dx, dy, dz, jacinv, nelv);
416}
417
445#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) && \
446 (__CUDA_ARCH__ < 1000) && NEKO_TMA_TOOLKIT
447
448template< const int LX, const int NW >
450void dudxyz_dmma_tma_elem(double * __restrict__ du,
451 const double * __restrict__ u,
452 const double * __restrict__ dr,
453 const double * __restrict__ ds,
454 const double * __restrict__ dt,
455 const double * __restrict__ dx,
456 const double * __restrict__ dy,
457 const double * __restrict__ dz,
458 const double * __restrict__ jacinv) {
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, which costs nothing here since
462 every one of them is a whole number of 128 byte lines */
464 __shared__ __align__(128) double shdy[DMMA_MAT];
465 __shared__ __align__(128) double shdz[DMMA_MAT];
466
467 /* shu carries the input and then the output, shr, shs and sht the reference
468 derivatives */
469 __shared__ __align__(128) double shu[DMMA_CUBE];
470 __shared__ __align__(128) double shr[DMMA_CUBE];
471 __shared__ __align__(128) double shs[DMMA_CUBE];
472 __shared__ __align__(128) double sht[DMMA_CUBE];
473
474 /* dr, ds, dt and jacinv */
475 __shared__ __align__(128) double shgr[DMMA_CUBE];
476 __shared__ __align__(128) double shgs[DMMA_CUBE];
477 __shared__ __align__(128) double shgt[DMMA_CUBE];
478 __shared__ __align__(128) double shgj[DMMA_CUBE];
479
480 __shared__ __align__(8) unsigned long long bar_u;
481 __shared__ __align__(8) unsigned long long bar_g;
482
484 sizeof(shdy) +
485 sizeof(shdz) +
486 sizeof(shu) +
487 sizeof(shr) +
488 sizeof(shs) +
489 sizeof(sht) +
490 sizeof(shgr) +
491 sizeof(shgs) +
492 sizeof(shgt) +
493 sizeof(shgj) +
494 sizeof(bar_u) +
495 sizeof(bar_g)
498
499 /* Only lx == DMMA_P stages as a contiguous run of bytes, which is what a
500 bulk copy moves; see the scope note in dmma_tma_kernel.h. Keep in step
501 with dmma_tma_dudxyz_lx_supported() */
504
505 enum { CUBE_BYTES = DMMA_CUBE * (int) sizeof(double),
506 NG = 4 };
507
508 const int nthrds = 32 * NW;
509 const int tid = threadIdx.x;
510 const int wf = tid >> 5;
511 const int ebase = blockIdx.x * DMMA_CUBE;
512
513 if (tid == 0) {
514 tma_barrier_init(&bar_u, 1);
515 tma_barrier_init(&bar_g, 1);
516 }
517
518 /* At LX == DMMA_P the derivative matrix fills the staged one exactly, and
519 there is no padding anywhere to zero */
520 for (int p = tid; p < DMMA_MAT; p += nthrds) {
521 shdx[p] = dx[p];
522 shdy[p] = dy[p];
523 shdz[p] = dz[p];
524 }
525
526 /* Both barriers initialised and every derivative matrix staged before
527 anyone waits on the one or contracts with the other */
529
530 if (tid == 0) {
531 tma_expect(&bar_u, CUBE_BYTES);
532 tma_load(shu, u + ebase, CUBE_BYTES, &bar_u);
533
534 tma_expect(&bar_g, NG * CUBE_BYTES);
535 tma_load(shgr, dr + ebase, CUBE_BYTES, &bar_g);
536 tma_load(shgs, ds + ebase, CUBE_BYTES, &bar_g);
537 tma_load(shgt, dt + ebase, CUBE_BYTES, &bar_g);
538 tma_load(shgj, jacinv + ebase, CUBE_BYTES, &bar_g);
539 }
540
541 /* u only. The four factor cubes are still arriving */
542 tma_wait(&bar_u, 0);
543
547
549
550 tma_wait(&bar_g, 0);
551
552 /* The staged input is dead once the contractions have been read out of it,
553 which the barrier above guarantees, so the result is formed in it and
554 leaves as one bulk store rather than DMMA_CUBE scalar ones */
555 for (int p = tid; p < DMMA_CUBE; p += nthrds) {
556 shu[p] = shgj[p] * ((shr[p] * shgr[p])
557 + (shs[p] * shgs[p])
558 + (sht[p] * shgt[p]));
559 }
560
561 /* The pointwise pass wrote shu through the generic proxy and the bulk store
562 reads it through the async one, so the fence is needed on top of the
563 barrier; see tma_fence_shared() */
566
567 if (tid == 0) {
570 }
571
572 /* The store reads shu asynchronously, and shared memory lives only as long
573 as the block does: nothing may retire until it has been read out */
575}
576
577#endif // __CUDA_ARCH__ == sm_90 with a CUDA 12 toolkit
578
579/*
580 * Compile-time dispatch onto the TMA staged DMMA element kernel, see the note
581 * on dudxyz_dmma_dispatch above. The no-op covers everything the strategy
582 * does not: single precision, any lx but DMMA_P, a build without sm_90, and a
583 * toolkit older than CUDA 12.
584 */
585template< typename T, const int LX, const int NW >
588 const T * __restrict__,
589 const T * __restrict__,
590 const T * __restrict__,
591 const T * __restrict__,
592 const T * __restrict__,
593 const T * __restrict__,
594 const T * __restrict__,
595 const T * __restrict__) { }
596};
597
598#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) && \
599 (__CUDA_ARCH__ < 1000) && NEKO_TMA_TOOLKIT
600
601/* Keep in sync with dmma_tma_dudxyz_lx_supported() in dmma_tma_kernel.h */
602#define NEKO_DUDXYZ_DMMA_TMA_DISPATCH(LXV) \
603 template< const int NW > \
604 struct dudxyz_dmma_tma_dispatch< double, LXV, NW > { \
605 __device__ static void run(double * __restrict__ du, \
606 const double * __restrict__ u, \
607 const double * __restrict__ dr, \
608 const double * __restrict__ ds, \
609 const double * __restrict__ dt, \
610 const double * __restrict__ dx, \
611 const double * __restrict__ dy, \
612 const double * __restrict__ dz, \
613 const double * __restrict__ jacinv) { \
614 dudxyz_dmma_tma_elem< LXV, NW >(du, u, dr, ds, dt, \
615 dx, dy, dz, jacinv); \
616 } \
617 }
618
620
621#endif // __CUDA_ARCH__ == sm_90 with a CUDA 12 toolkit
622
623template< typename T, const int LX, const int NW >
626 const T * __restrict__ u,
627 const T * __restrict__ dr,
628 const T * __restrict__ ds,
629 const T * __restrict__ dt,
630 const T * __restrict__ dx,
631 const T * __restrict__ dy,
632 const T * __restrict__ dz,
633 const T * __restrict__ jacinv) {
634
636 dx, dy, dz, jacinv);
637}
638
639#endif // __MATH_DUDXYZ_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)
__shared__ T shu[LX *LX]
__global__ void const T *__restrict__ x
const bool active
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dz
__global__ void const T *__restrict__ const T *__restrict__ dr
const int sh
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dy
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ ds
T ru[LX]
const int eb
__shared__ T shdy[LX *LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dt
T rdt[LX]
const int i
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dx
const int ij
T rjacinv[LX]
__shared__ T shdx[LX *LX]
const int e
T rds[LX]
__shared__ T shdz[LX *LX]
const int e_blk
__global__ void dudxyz_kernel_1d(T *__restrict__ du, const T *__restrict__ u, const T *__restrict__ dr, const T *__restrict__ ds, const T *__restrict__ dt, const T *__restrict__ dx, const T *__restrict__ dy, const T *__restrict__ dz, const T *__restrict__ jacinv)
const int ele
__global__ void const T *__restrict__ u
__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
const int j
T rdr[LX]
__syncthreads()
__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__ jacinv
#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__)