Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
ax_helm_compr_kernel.h
Go to the documentation of this file.
1#ifndef __MATH_AX_HELM_COMPR_KERNEL_H__
2#define __MATH_AX_HELM_COMPR_KERNEL_H__
3/*
4 Copyright (c) 2026, The Neko Authors
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 * Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13
14 * Redistributions in binary form must reproduce the above
15 copyright notice, this list of conditions and the following
16 disclaimer in the documentation and/or other materials provided
17 with the distribution.
18
19 * Neither the name of the authors nor the names of its
20 contributors may be used to endorse or promote products derived
21 from this software without specific prior written permission.
22
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 POSSIBILITY OF SUCH DAMAGE.
35*/
36
37/*
38 * Compressed geometric factors: every strategy of ax_helm_kernel.h, with each
39 * of its tuning candidates, reading g11 .. g23 from one copy per distinct
40 * element shape rather than one per element.
41 *
42 * Element e reads copy c = ind[e] - 1 -- ind is coef%compression_inds, which
43 * is 1-based -- so every geometric factor access g[ijk + e * LX^3] becomes
44 * g[ijk + c * LX^3]; the copies have the layout of the uncompressed arrays,
45 * point fastest. u, h1 and the output keep the element index and nothing else
46 * differs, so the arithmetic, and the result, is that of the standard kernels
47 * on the expanded factors. The strategies that hold more than one element
48 * take the copy of each: the element blocked kstep kernels per threadIdx.z
49 * plane (tail slots clamp to the last element, whose copy they then read),
50 * the packed DMMA kernel per point through the element its pack map resolves
51 * to, and the TMA kernels bulk copy the factor cubes straight from the copy.
52 *
53 * Derived mechanically from ax_helm_kernel.h, whose notes all apply here too;
54 * a change to one belongs in the other. Every kernel, helper and macro carries
55 * a compr in its name so that both link into one device program, and the
56 * mass term of the vector operator, which reads no geometric factors, is not
57 * repeated: it is ax_helm_kernel_vector_part2 of ax_helm_kernel.h.
58 */
59
60#include "elem_block.h"
61#include "dmma_kernel.h"
62#include "dmma_tma_kernel.h"
63
64/*
65 * A note on elements per block for the vector kstep kernels.
66 *
67 * These hold three times the register blocked state of the scalar ones -- six
68 * T[LX] arrays rather than two -- and measure at 254-255 registers on sm_90
69 * for every lx from 8 up, spilling at 10, 12, 14 and 16. Blocking is therefore
70 * not expected to buy much: it does not change registers per thread, only
71 * threads per block, so at 255 registers the occupancy is the same either way
72 * and all it saves is loading the derivative matrices once per block instead
73 * of once per element.
74 *
75 * They are swept anyway, over the same elem_block<> candidates as the scalar
76 * kernels. "Not expected to buy much" is a prediction, and pinning it at build
77 * time is a prediction the tuner can never check. elem_block<>'s thread clamp
78 * keeps every candidate inside the shared memory budget at every lx -- the
79 * widest case, lx = 11 at four elements per block, comes to 37 kB against the
80 * 48 kB cap -- so nothing here needs a bound of its own.
81 */
82
87template< typename T, const int LX, const int CHUNKS >
89 const T * __restrict__ u,
90 const T * __restrict__ dx,
91 const T * __restrict__ dy,
92 const T * __restrict__ dz,
93 const T * __restrict__ dxt,
94 const T * __restrict__ dyt,
95 const T * __restrict__ dzt,
96 const T * __restrict__ h1,
97 const T * __restrict__ g11,
98 const T * __restrict__ g22,
99 const T * __restrict__ g33,
100 const T * __restrict__ g12,
101 const T * __restrict__ g13,
102 const T * __restrict__ g23,
103 const int * __restrict__ ind) {
104
108
112
117
118 const int e = blockIdx.x;
119 const int ec = ind[e] - 1;
120 const int iii = threadIdx.x;
121 const int nchunks = (LX * LX * LX - 1)/CHUNKS + 1;
122
123 if (iii<LX*LX) {
124 shdx[iii] = dx[iii];
125 shdy[iii] = dy[iii];
126 shdz[iii] = dz[iii];
127 }
128
129 {
130 int i = iii;
131 while (i < LX * LX * LX){
132 shu[i] = u[i+e*LX*LX*LX];
133 i = i + CHUNKS;
134 }
135 }
136
138
139 if (iii<LX*LX){
140 shdxt[iii] = dxt[iii];
141 shdyt[iii] = dyt[iii];
142 shdzt[iii] = dzt[iii];
143 }
144
145 for (int n=0; n<nchunks; n++){
146 const int ijk = iii+n*CHUNKS;
147 const int jk = ijk/LX;
148 const int i = ijk-jk*LX;
149 const int k = jk/LX;
150 const int j = jk-k*LX;
151 if (i<LX && j<LX && k<LX && ijk < LX*LX*LX){
152 const T G00 = g11[ijk+ec*LX*LX*LX];
153 const T G11 = g22[ijk+ec*LX*LX*LX];
154 const T G22 = g33[ijk+ec*LX*LX*LX];
155 const T G01 = g12[ijk+ec*LX*LX*LX];
156 const T G02 = g13[ijk+ec*LX*LX*LX];
157 const T G12 = g23[ijk+ec*LX*LX*LX];
158 const T H1 = h1[ijk+e*LX*LX*LX];
159 T rtmp = 0.0;
160 T stmp = 0.0;
161 T ttmp = 0.0;
162#pragma unroll
163 for (int l = 0; l<LX; l++){
164 rtmp = rtmp + shdx[i+l*LX] * shu[l+j*LX+k*LX*LX];
165 stmp = stmp + shdy[j+l*LX] * shu[i+l*LX+k*LX*LX];
166 ttmp = ttmp + shdz[k+l*LX] * shu[i+j*LX+l*LX*LX];
167 }
168 shur[ijk] = H1 * (G00 * rtmp + G01 * stmp + G02 * ttmp);
169 shus[ijk] = H1 * (G01 * rtmp + G11 * stmp + G12 * ttmp);
170 shut[ijk] = H1 * (G02 * rtmp + G12 * stmp + G22 * ttmp);
171 }
172 }
173
175
176 for (int n=0; n<nchunks; n++){
177 const int ijk = iii+n*CHUNKS;
178 const int jk = ijk/LX;
179 const int k = jk/LX;
180 const int j = jk-k*LX;
181 const int i = ijk-jk*LX;
182 if (i<LX && j<LX && k<LX && ijk <LX*LX*LX){
183 T wijke = 0.0;
184#pragma unroll
185 for (int l = 0; l<LX; l++){
186 wijke = wijke
187 + shdxt[i+l*LX] * shur[l+j*LX+k*LX*LX]
188 + shdyt[j+l*LX] * shus[i+l*LX+k*LX*LX]
189 + shdzt[k+l*LX] * shut[i+j*LX+l*LX*LX];
190 }
191 w[ijk+e*LX*LX*LX] = wijke;
192 }
193 }
194}
195
196template< typename T, const int LX, const int EB >
199 const T * __restrict__ u,
210 const int * __restrict__ ind,
211 const int nelv) {
212
213 /* Element independent, one copy per block */
214 __shared__ T shdx[LX * LX];
217
218 /* One slice per element in the block */
222
223 static_assert(sizeof(shdx) +
224 sizeof(shdy) +
225 sizeof(shdz) +
226 sizeof(shu) +
227 sizeof(shur) +
228 sizeof(shus)
230 "kstep block exceeds the shared memory budget");
231
235
236 /* Threads past the last element still have to reach the block wide
237 barriers below, so clamp their reads and drop their stores rather
238 than returning early. At EB == 1 the grid covers nelv exactly, so all
239 of this bookkeeping is constant folded and the kernel is exactly what
240 it was before blocking */
241 const int eb = (EB == 1) ? 0 : threadIdx.z;
242 const int e_blk = blockIdx.x * EB + eb;
243 const bool active = (EB == 1) ? true : (e_blk < nelv);
244 const int e = active ? e_blk : (nelv - 1);
245 const int j = threadIdx.y;
246 const int i = threadIdx.x;
247 const int ij = i + j*LX;
248 const int sh = eb*LX*LX;
249 const int ele = e*LX*LX*LX;
250 const int gele = (ind[e] - 1)*LX*LX*LX;
251
252 if (eb == 0) {
253 shdx[ij] = dx[ij];
254 shdy[ij] = dy[ij];
255 shdz[ij] = dz[ij];
256 }
257
258#pragma unroll
259 for(int k = 0; k < LX; ++k){
260 ru[k] = u[ij + k*LX*LX + ele];
261 rw[k] = 0.0;
262 }
263
264
266#pragma unroll
267 for (int k = 0; k < LX; ++k){
268 const int ijk = ij + k*LX*LX;
269 const T G00 = g11[ijk+gele];
270 const T G11 = g22[ijk+gele];
271 const T G22 = g33[ijk+gele];
272 const T G01 = g12[ijk+gele];
273 const T G02 = g13[ijk+gele];
274 const T G12 = g23[ijk+gele];
275 const T H1 = h1[ijk+ele];
276 T ttmp = 0.0;
277 shu[sh + ij] = ru[k];
278#pragma unroll
279 for (int l = 0; l < LX; l++){
280 ttmp += shdz[k+l*LX] * ru[l];
281 }
283
284 T rtmp = 0.0;
285 T stmp = 0.0;
286#pragma unroll
287 for (int l = 0; l < LX; l++){
288 rtmp += shdx[i+l*LX] * shu[sh + l+j*LX];
289 stmp += shdy[j+l*LX] * shu[sh + i+l*LX];
290 }
291 shur[sh + ij] = H1
292 * (G00 * rtmp
293 + G01 * stmp
294 + G02 * ttmp);
295 shus[sh + ij] = H1
296 * (G01 * rtmp
297 + G11 * stmp
298 + G12 * ttmp);
299 rut = H1
300 * (G02 * rtmp
301 + G12 * stmp
302 + G22 * ttmp);
303
305
306 T wijke = 0.0;
307#pragma unroll
308 for (int l = 0; l < LX; l++){
309 wijke += shur[sh + l+j*LX] * shdx[l+i*LX];
310 rw[l] += rut * shdz[k+l*LX];
311 wijke += shus[sh + i+l*LX] * shdy[l + j*LX];
312 }
313 rw[k] += wijke;
314 }
315 if (active) {
316#pragma unroll
317 for (int k = 0; k < LX; ++k){
318 w[ij + k*LX*LX + ele] = rw[k];
319 }
320 }
321}
322
328template< typename T, const int LX, const int EB >
331 const T * __restrict__ u,
332 const T * __restrict__ dx,
333 const T * __restrict__ dy,
334 const T * __restrict__ dz,
335 const T * __restrict__ h1,
336 const T * __restrict__ g11,
337 const T * __restrict__ g22,
338 const T * __restrict__ g33,
339 const T * __restrict__ g12,
340 const T * __restrict__ g13,
341 const T * __restrict__ g23,
342 const int * __restrict__ ind,
343 const int nelv) {
344
345 /* Element independent, one copy per block */
346 __shared__ T shdx[LX * (LX+1)];
347 __shared__ T shdy[LX * (LX+1)];
348 __shared__ T shdz[LX * (LX+1)];
349
350 /* One slice per element in the block */
351 __shared__ T shu[EB * LX * (LX+1)];
352 __shared__ T shur[EB * LX * LX]; // only accessed using fastest dimension
353 __shared__ T shus[EB * LX * (LX+1)];
354
355 static_assert(sizeof(shdx) +
356 sizeof(shdy) +
357 sizeof(shdz) +
358 sizeof(shu) +
359 sizeof(shur) +
360 sizeof(shus)
362 "kstep block exceeds the shared memory budget");
363
364 T ru[LX];
365 T rw[LX];
366 T rut;
367
368 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
369 constant folded away and the kernel is exactly what it was before */
370 const int eb = (EB == 1) ? 0 : threadIdx.z;
371 const int e_blk = blockIdx.x * EB + eb;
372 const bool active = (EB == 1) ? true : (e_blk < nelv);
373 const int e = active ? e_blk : (nelv - 1);
374 const int j = threadIdx.y;
375 const int i = threadIdx.x;
376 const int ij = i + j*LX;
377 const int ij_p = i + j*(LX+1);
378 const int sh = eb*LX*LX;
379 const int sh_p = eb*LX*(LX+1);
380 const int ele = e*LX*LX*LX;
381 const int gele = (ind[e] - 1)*LX*LX*LX;
382
383 if (eb == 0) {
384 shdx[ij_p] = dx[ij];
385 shdy[ij_p] = dy[ij];
386 shdz[ij_p] = dz[ij];
387 }
388
389#pragma unroll
390 for(int k = 0; k < LX; ++k){
391 ru[k] = u[ij + k*LX*LX + ele];
392 rw[k] = 0.0;
393 }
394
395
397#pragma unroll
398 for (int k = 0; k < LX; ++k){
399 const int ijk = ij + k*LX*LX;
400 const T G00 = g11[ijk+gele];
401 const T G11 = g22[ijk+gele];
402 const T G22 = g33[ijk+gele];
403 const T G01 = g12[ijk+gele];
404 const T G02 = g13[ijk+gele];
405 const T G12 = g23[ijk+gele];
406 const T H1 = h1[ijk+ele];
407 T ttmp = 0.0;
408 shu[sh_p + ij_p] = ru[k];
409#pragma unroll
410 for (int l = 0; l < LX; l++){
411 ttmp += shdz[k+l*(LX+1)] * ru[l];
412 }
414
415 T rtmp = 0.0;
416 T stmp = 0.0;
417#pragma unroll
418 for (int l = 0; l < LX; l++){
419 rtmp += shdx[i+l*(LX+1)] * shu[sh_p + l+j*(LX+1)];
420 stmp += shdy[j+l*(LX+1)] * shu[sh_p + i+l*(LX+1)];
421 }
422 shur[sh + ij] = H1
423 * (G00 * rtmp
424 + G01 * stmp
425 + G02 * ttmp);
426 shus[sh_p + ij_p] = H1
427 * (G01 * rtmp
428 + G11 * stmp
429 + G12 * ttmp);
430 rut = H1
431 * (G02 * rtmp
432 + G12 * stmp
433 + G22 * ttmp);
434
436
437 T wijke = 0.0;
438#pragma unroll
439 for (int l = 0; l < LX; l++){
440 wijke += shur[sh + l+j*LX] * shdx[l+i*(LX+1)];
441 rw[l] += rut * shdz[k+l*(LX+1)];
442 wijke += shus[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
443 }
444 rw[k] += wijke;
445 }
446 if (active) {
447#pragma unroll
448 for (int k = 0; k < LX; ++k){
449 w[ij + k*LX*LX + ele] = rw[k];
450 }
451 }
452}
453
454
467#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 800) && (__CUDA_ARCH__ < 1000)
468
469template< const int LX, const int NW >
472 const double * __restrict__ u,
473 const double * __restrict__ dx,
474 const double * __restrict__ dy,
475 const double * __restrict__ dz,
476 const double * __restrict__ h1,
477 const double * __restrict__ g11,
478 const double * __restrict__ g22,
479 const double * __restrict__ g33,
480 const double * __restrict__ g12,
481 const double * __restrict__ g13,
482 const double * __restrict__ g23,
483 const int * __restrict__ ind,
484 const int nelv) {
485
486 /* Element independent, one copy per block. Block diagonal when more than
487 one element is packed: one LX x LX copy of D per sub-cube */
489 __shared__ __align__(16) double shdy[DMMA_MAT];
490 __shared__ __align__(16) double shdz[DMMA_MAT];
491
492 /* The pack, padded to DMMA_P^3. shu carries the input and then the
493 output, shr, shs and sht the reference derivatives */
494 __shared__ __align__(16) double shu[DMMA_CUBE];
495 __shared__ __align__(16) double shr[DMMA_CUBE];
496 __shared__ __align__(16) double shs[DMMA_CUBE];
497 __shared__ __align__(16) double sht[DMMA_CUBE];
498
500 sizeof(shdy) +
501 sizeof(shdz) +
502 sizeof(shu) +
503 sizeof(shr) +
504 sizeof(shs) +
505 sizeof(sht)
508
509 /* Elements per sub-cube axis, and per cube, see the note in dmma_kernel.h.
510 At PPA == 1 the addressing is not left to constant fold -- dmma_pack is
511 specialised so the tail clamp and the guarded store are never emitted at
512 all, see the note there */
513 enum { PPA = (DMMA_P % LX == 0) ? (DMMA_P / LX) : 1,
514 PACK = PPA * PPA * PPA,
515 LX3 = LX * LX * LX,
516 NP = PACK * LX3 };
517
519
520 const int nthrds = 32 * NW;
521 const int tid = threadIdx.x;
522 const int wf = tid >> 5;
523 const int ebase = pack::ebase();
524
525 /* The padding has to be finite, see the note above. At LX == DMMA_P with
526 one element packed there is none and this is folded away */
527 if (PACK * LX3 < DMMA_CUBE) {
528 for (int p = tid; p < DMMA_CUBE; p += nthrds) {
529 shu[p] = 0.0;
530 }
531 }
532 if (LX < DMMA_P) {
533 for (int p = tid; p < DMMA_MAT; p += nthrds) {
534 shdx[p] = 0.0;
535 shdy[p] = 0.0;
536 shdz[p] = 0.0;
537 }
538 }
539 if (LX < DMMA_P) {
541 }
542
543 /* One copy of D per sub-cube, down the diagonal */
544 for (int p = tid; p < LX * LX; p += nthrds) {
545 const int i = p % LX;
546 const int l = p / LX;
547#pragma unroll
548 for (int b = 0; b < PPA; b++) {
549 const int m = (b * LX + i) + DMMA_P * (b * LX + l);
550 shdx[m] = dx[p];
551 shdy[m] = dy[p];
552 shdz[m] = dz[p];
553 }
554 }
555
556 for (int p = tid; p < NP; p += nthrds) {
557 const dmma_idx x = pack::map(p, ebase, nelv);
558
559 shu[x.c] = u[x.g];
560 }
561
563
567
569
570 for (int p = tid; p < NP; p += nthrds) {
571 const dmma_idx x = pack::map(p, ebase, nelv);
572 const int c = x.c;
573 const int gp = x.g;
574 const int gq = x.l + (ind[x.g / LX3] - 1) * LX3;
575
576 const double G00 = g11[gq];
577 const double G11 = g22[gq];
578 const double G22 = g33[gq];
579 const double G01 = g12[gq];
580 const double G02 = g13[gq];
581 const double G12 = g23[gq];
582 const double H1 = h1[gp];
583
584 const double rtmp = shr[c];
585 const double stmp = shs[c];
586 const double ttmp = sht[c];
587
588 shr[c] = H1
589 * (G00 * rtmp
590 + G01 * stmp
591 + G02 * ttmp);
592 shs[c] = H1
593 * (G01 * rtmp
594 + G11 * stmp
595 + G12 * ttmp);
596 sht[c] = H1
597 * (G02 * rtmp
598 + G12 * stmp
599 + G22 * ttmp);
600 }
601
603
604 /* The result overwrites the staged input; the first contraction writes
605 every tile of the cube, so nothing has to be cleared first. The barriers
606 are needed because the j slab tiles of axis 2 span what every warp wrote
607 along axis 0 and 1 */
614
615 for (int p = tid; p < NP; p += nthrds) {
616 const dmma_idx x = pack::map(p, ebase, nelv);
617
618 if (x.live) {
619 w[x.g] = shu[x.c];
620 }
621 }
622}
623
624#endif // __CUDA_ARCH__ in [800, 1000)
625
626/*
627 * Compile-time dispatch onto the DMMA element kernel. The launch macros in
628 * ax_helm_compr.cu are written for every LX the operator dispatches and for
629 * whatever `real` is, so every combination has to compile; the ones the
630 * strategy does not cover -- single precision, LX outside the supported range,
631 * a build without an fp64 tensor core arch -- resolve to this no-op. The
632 * autotuner never selects the strategy for them, so the no-op is unreachable at
633 * runtime, see dmma_lx_supported() and cuda_have_dmma() in dmma_kernel.h.
634 */
635template< typename T, const int LX, const int NW >
638 const T * __restrict__,
639 const T * __restrict__,
640 const T * __restrict__,
641 const T * __restrict__,
642 const T * __restrict__,
643 const T * __restrict__,
644 const T * __restrict__,
645 const T * __restrict__,
646 const T * __restrict__,
647 const T * __restrict__,
648 const T * __restrict__,
649 const int * __restrict__,
650 const int) { }
651};
652
653#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 800) && (__CUDA_ARCH__ < 1000)
654
655/* Keep in sync with dmma_lx_supported() in dmma_kernel.h */
656#define NEKO_AX_HELM_COMPR_DMMA_DISPATCH(LXV) \
657 template< const int NW > \
658 struct ax_helm_compr_dmma_dispatch< double, LXV, NW > { \
659 __device__ static void run(double * __restrict__ w, \
660 const double * __restrict__ u, \
661 const double * __restrict__ dx, \
662 const double * __restrict__ dy, \
663 const double * __restrict__ dz, \
664 const double * __restrict__ h1, \
665 const double * __restrict__ g11, \
666 const double * __restrict__ g22, \
667 const double * __restrict__ g33, \
668 const double * __restrict__ g12, \
669 const double * __restrict__ g13, \
670 const double * __restrict__ g23, \
671 const int * __restrict__ ind, \
672 const int nelv) { \
673 ax_helm_compr_dmma_elem< LXV, NW >(w, u, dx, dy, dz, h1, g11, g22, g33, \
674 g12, g13, g23, ind, nelv); \
675 } \
676 }
677
685
686#endif // __CUDA_ARCH__ in [800, 1000)
687
688template< typename T, const int LX, const int NW >
691 const T * __restrict__ u,
692 const T * __restrict__ dx,
693 const T * __restrict__ dy,
694 const T * __restrict__ dz,
695 const T * __restrict__ h1,
696 const T * __restrict__ g11,
697 const T * __restrict__ g22,
698 const T * __restrict__ g33,
699 const T * __restrict__ g12,
700 const T * __restrict__ g13,
701 const T * __restrict__ g23,
702 const int * __restrict__ ind,
703 const int nelv) {
704
706 g33, g12, g13, g23, ind, nelv);
707}
708
730#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) && \
731 (__CUDA_ARCH__ < 1000) && NEKO_TMA_TOOLKIT
732
733template< const int LX, const int NW >
736 const double * __restrict__ u,
737 const double * __restrict__ dx,
738 const double * __restrict__ dy,
739 const double * __restrict__ dz,
740 const double * __restrict__ h1,
741 const double * __restrict__ g11,
742 const double * __restrict__ g22,
743 const double * __restrict__ g33,
744 const double * __restrict__ g12,
745 const double * __restrict__ g13,
746 const double * __restrict__ g23,
747 const int * __restrict__ ind) {
748
749 /* A bulk copy needs 16 byte alignment at both ends; the cubes are given the
750 128 the tensor variants would want anyway, which costs nothing here since
751 every one of them is a whole number of 128 byte lines */
753 __shared__ __align__(128) double shdy[DMMA_MAT];
754 __shared__ __align__(128) double shdz[DMMA_MAT];
755
756 /* shu carries the input and then the output, shr, shs and sht the reference
757 derivatives */
758 __shared__ __align__(128) double shu[DMMA_CUBE];
759 __shared__ __align__(128) double shr[DMMA_CUBE];
760 __shared__ __align__(128) double shs[DMMA_CUBE];
761 __shared__ __align__(128) double sht[DMMA_CUBE];
762
763 /* h1, g11, g22, g33, g12, g13, g23 */
765
766 __shared__ __align__(8) unsigned long long bar_u;
767 __shared__ __align__(8) unsigned long long bar_g;
768
770 sizeof(shdy) +
771 sizeof(shdz) +
772 sizeof(shu) +
773 sizeof(shr) +
774 sizeof(shs) +
775 sizeof(sht) +
776 sizeof(shg) +
777 sizeof(bar_u) +
778 sizeof(bar_g)
781
782 /* Only lx == DMMA_P stages as a contiguous run of bytes, which is what a
783 bulk copy moves; see the scope note in dmma_tma_kernel.h. Keep in step
784 with dmma_tma_lx_supported() */
787
788 enum { CUBE_BYTES = DMMA_CUBE * (int) sizeof(double) };
789
790 const int nthrds = 32 * NW;
791 const int tid = threadIdx.x;
792 const int wf = tid >> 5;
793 const int ebase = blockIdx.x * DMMA_CUBE;
794 const int gbase = (ind[blockIdx.x] - 1) * DMMA_CUBE;
795
796 if (tid == 0) {
797 tma_barrier_init(&bar_u, 1);
798 tma_barrier_init(&bar_g, 1);
799 }
800
801 /* At LX == DMMA_P the derivative matrix fills the staged one exactly, and
802 there is no padding anywhere to zero */
803 for (int p = tid; p < DMMA_MAT; p += nthrds) {
804 shdx[p] = dx[p];
805 shdy[p] = dy[p];
806 shdz[p] = dz[p];
807 }
808
809 /* Both barriers initialised and both derivative matrices staged before
810 anyone waits on the one or contracts with the other */
812
813 if (tid == 0) {
814 tma_expect(&bar_u, CUBE_BYTES);
815 tma_load(shu, u + ebase, CUBE_BYTES, &bar_u);
816
817 tma_expect(&bar_g, DMMA_NG * CUBE_BYTES);
818 tma_load(shg[0], h1 + ebase, CUBE_BYTES, &bar_g);
819 tma_load(shg[1], g11 + gbase, CUBE_BYTES, &bar_g);
820 tma_load(shg[2], g22 + gbase, CUBE_BYTES, &bar_g);
821 tma_load(shg[3], g33 + gbase, CUBE_BYTES, &bar_g);
822 tma_load(shg[4], g12 + gbase, CUBE_BYTES, &bar_g);
823 tma_load(shg[5], g13 + gbase, CUBE_BYTES, &bar_g);
824 tma_load(shg[6], g23 + gbase, CUBE_BYTES, &bar_g);
825 }
826
827 /* u only. The seven factor cubes are still arriving */
828 tma_wait(&bar_u, 0);
829
833
835
836 tma_wait(&bar_g, 0);
837
838 for (int p = tid; p < DMMA_CUBE; p += nthrds) {
839 const double H1 = shg[0][p];
840 const double G00 = shg[1][p];
841 const double G11 = shg[2][p];
842 const double G22 = shg[3][p];
843 const double G01 = shg[4][p];
844 const double G02 = shg[5][p];
845 const double G12 = shg[6][p];
846
847 const double rtmp = shr[p];
848 const double stmp = shs[p];
849 const double ttmp = sht[p];
850
851 shr[p] = H1
852 * (G00 * rtmp
853 + G01 * stmp
854 + G02 * ttmp);
855 shs[p] = H1
856 * (G01 * rtmp
857 + G11 * stmp
858 + G12 * ttmp);
859 sht[p] = H1
860 * (G02 * rtmp
861 + G12 * stmp
862 + G22 * ttmp);
863 }
864
866
867 /* The result overwrites the staged input, exactly as in the scalar dmma
868 kernel; see the note there on why the barriers between these are needed */
874
875 /* The contractions wrote shu through the generic proxy and the bulk store
876 reads it through the async one, so the fence is needed on top of the
877 barrier; see tma_fence_shared() */
880
881 if (tid == 0) {
884 }
885
886 /* The store reads shu asynchronously, and shared memory lives only as long
887 as the block does: nothing may retire until it has been read out */
889}
890
891#endif // __CUDA_ARCH__ == sm_90 with a CUDA 12 toolkit
892
893/*
894 * Compile-time dispatch onto the TMA staged DMMA element kernel, see the note
895 * on ax_helm_compr_dmma_dispatch above. The no-op covers everything the
896 * strategy does not: single precision, any lx but DMMA_P, a build without
897 * sm_90, and a toolkit older than CUDA 12.
898 */
899template< typename T, const int LX, const int NW >
902 const T * __restrict__,
903 const T * __restrict__,
904 const T * __restrict__,
905 const T * __restrict__,
906 const T * __restrict__,
907 const T * __restrict__,
908 const T * __restrict__,
909 const T * __restrict__,
910 const T * __restrict__,
911 const T * __restrict__,
912 const T * __restrict__,
913 const int * __restrict__) { }
914};
915
916#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) && \
917 (__CUDA_ARCH__ < 1000) && NEKO_TMA_TOOLKIT
918
919/* Keep in sync with dmma_tma_lx_supported() in dmma_tma_kernel.h */
920#define NEKO_AX_HELM_COMPR_DMMA_TMA_DISPATCH(LXV) \
921 template< const int NW > \
922 struct ax_helm_compr_dmma_tma_dispatch< double, LXV, NW > { \
923 __device__ static void run(double * __restrict__ w, \
924 const double * __restrict__ u, \
925 const double * __restrict__ dx, \
926 const double * __restrict__ dy, \
927 const double * __restrict__ dz, \
928 const double * __restrict__ h1, \
929 const double * __restrict__ g11, \
930 const double * __restrict__ g22, \
931 const double * __restrict__ g33, \
932 const double * __restrict__ g12, \
933 const double * __restrict__ g13, \
934 const double * __restrict__ g23, \
935 const int * __restrict__ ind) { \
936 ax_helm_compr_dmma_tma_elem< LXV, NW >(w, u, dx, dy, dz, h1, g11, g22, \
937 g33, g12, g13, g23, ind); \
938 } \
939 }
940
942
943#endif // __CUDA_ARCH__ == sm_90 with a CUDA 12 toolkit
944
945template< typename T, const int LX, const int NW >
948 const T * __restrict__ u,
949 const T * __restrict__ dx,
950 const T * __restrict__ dy,
951 const T * __restrict__ dz,
952 const T * __restrict__ h1,
953 const T * __restrict__ g11,
954 const T * __restrict__ g22,
955 const T * __restrict__ g33,
956 const T * __restrict__ g12,
957 const T * __restrict__ g13,
958 const T * __restrict__ g23,
959 const int * __restrict__ ind) {
960
962 g22, g33, g12, g13, g23,
963 ind);
964}
965
966
967/*
968 * Vector versions
969 */
970
971template< typename T, const int LX, const int EB >
976 const T * __restrict__ u,
977 const T * __restrict__ v,
978 const T * __restrict__ w,
979 const T * __restrict__ dx,
980 const T * __restrict__ dy,
981 const T * __restrict__ dz,
982 const T * __restrict__ h1,
983 const T * __restrict__ g11,
984 const T * __restrict__ g22,
985 const T * __restrict__ g33,
986 const T * __restrict__ g12,
987 const T * __restrict__ g13,
988 const T * __restrict__ g23,
989 const int * __restrict__ ind,
990 const int nelv) {
991
992 /* Element independent, one copy per block */
993 __shared__ T shdx[LX * LX];
994 __shared__ T shdy[LX * LX];
995 __shared__ T shdz[LX * LX];
996
997 /* One slice per element in the block */
998 __shared__ T shu[EB * LX * LX];
999 __shared__ T shur[EB * LX * LX];
1000 __shared__ T shus[EB * LX * LX];
1001
1005
1009
1010 static_assert(sizeof(shdx) +
1011 sizeof(shdy) +
1012 sizeof(shdz) +
1013 sizeof(shu) +
1014 sizeof(shur) +
1015 sizeof(shus) +
1016 sizeof(shv) +
1017 sizeof(shvr) +
1018 sizeof(shvs) +
1019 sizeof(shw) +
1020 sizeof(shwr) +
1021 sizeof(shws)
1023 "kstep block exceeds the shared memory budget");
1024
1025 T ru[LX];
1027 T rw[LX];
1028
1032
1033 T rut;
1036
1037 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
1038 constant folded away and the kernel is exactly what it was before */
1039 const int eb = (EB == 1) ? 0 : threadIdx.z;
1040 const int e_blk = blockIdx.x * EB + eb;
1041 const bool active = (EB == 1) ? true : (e_blk < nelv);
1042 const int e = active ? e_blk : (nelv - 1);
1043 const int j = threadIdx.y;
1044 const int i = threadIdx.x;
1045 const int ij = i + j*LX;
1046 const int sh = eb*LX*LX;
1047 const int ele = e*LX*LX*LX;
1048 const int gele = (ind[e] - 1)*LX*LX*LX;
1049
1050 if (eb == 0) {
1051 shdx[ij] = dx[ij];
1052 shdy[ij] = dy[ij];
1053 shdz[ij] = dz[ij];
1054 }
1055
1056#pragma unroll
1057 for(int k = 0; k < LX; ++k){
1058 ru[k] = u[ij + k*LX*LX + ele];
1059 ruw[k] = 0.0;
1060
1061 rv[k] = v[ij + k*LX*LX + ele];
1062 rvw[k] = 0.0;
1063
1064 rw[k] = w[ij + k*LX*LX + ele];
1065 rww[k] = 0.0;
1066 }
1067
1068
1069 __syncthreads();
1070#pragma unroll
1071 for (int k = 0; k < LX; ++k){
1072 const int ijk = ij + k*LX*LX;
1073 const T G00 = g11[ijk+gele];
1074 const T G11 = g22[ijk+gele];
1075 const T G22 = g33[ijk+gele];
1076 const T G01 = g12[ijk+gele];
1077 const T G02 = g13[ijk+gele];
1078 const T G12 = g23[ijk+gele];
1079 const T H1 = h1[ijk+ele];
1080 T uttmp = 0.0;
1081 T vttmp = 0.0;
1082 T wttmp = 0.0;
1083 shu[sh + ij] = ru[k];
1084 shv[sh + ij] = rv[k];
1085 shw[sh + ij] = rw[k];
1086#pragma unroll
1087 for (int l = 0; l < LX; l++){
1088 uttmp += shdz[k+l*LX] * ru[l];
1089 vttmp += shdz[k+l*LX] * rv[l];
1090 wttmp += shdz[k+l*LX] * rw[l];
1091 }
1092 __syncthreads();
1093
1094 T urtmp = 0.0;
1095 T ustmp = 0.0;
1096
1097 T vrtmp = 0.0;
1098 T vstmp = 0.0;
1099
1100 T wrtmp = 0.0;
1101 T wstmp = 0.0;
1102#pragma unroll
1103 for (int l = 0; l < LX; l++){
1104 urtmp += shdx[i+l*LX] * shu[sh + l+j*LX];
1105 ustmp += shdy[j+l*LX] * shu[sh + i+l*LX];
1106
1107 vrtmp += shdx[i+l*LX] * shv[sh + l+j*LX];
1108 vstmp += shdy[j+l*LX] * shv[sh + i+l*LX];
1109
1110 wrtmp += shdx[i+l*LX] * shw[sh + l+j*LX];
1111 wstmp += shdy[j+l*LX] * shw[sh + i+l*LX];
1112 }
1113
1114 shur[sh + ij] = H1
1115 * (G00 * urtmp
1116 + G01 * ustmp
1117 + G02 * uttmp);
1118 shus[sh + ij] = H1
1119 * (G01 * urtmp
1120 + G11 * ustmp
1121 + G12 * uttmp);
1122 rut = H1
1123 * (G02 * urtmp
1124 + G12 * ustmp
1125 + G22 * uttmp);
1126
1127 shvr[sh + ij] = H1
1128 * (G00 * vrtmp
1129 + G01 * vstmp
1130 + G02 * vttmp);
1131 shvs[sh + ij] = H1
1132 * (G01 * vrtmp
1133 + G11 * vstmp
1134 + G12 * vttmp);
1135 rvt = H1
1136 * (G02 * vrtmp
1137 + G12 * vstmp
1138 + G22 * vttmp);
1139
1140 shwr[sh + ij] = H1
1141 * (G00 * wrtmp
1142 + G01 * wstmp
1143 + G02 * wttmp);
1144 shws[sh + ij] = H1
1145 * (G01 * wrtmp
1146 + G11 * wstmp
1147 + G12 * wttmp);
1148 rwt = H1
1149 * (G02 * wrtmp
1150 + G12 * wstmp
1151 + G22 * wttmp);
1152
1153 __syncthreads();
1154
1155 T uwijke = 0.0;
1156 T vwijke = 0.0;
1157 T wwijke = 0.0;
1158#pragma unroll
1159 for (int l = 0; l < LX; l++){
1160 uwijke += shur[sh + l+j*LX] * shdx[l+i*LX];
1161 ruw[l] += rut * shdz[k+l*LX];
1162 uwijke += shus[sh + i+l*LX] * shdy[l + j*LX];
1163
1164 vwijke += shvr[sh + l+j*LX] * shdx[l+i*LX];
1165 rvw[l] += rvt * shdz[k+l*LX];
1166 vwijke += shvs[sh + i+l*LX] * shdy[l + j*LX];
1167
1168 wwijke += shwr[sh + l+j*LX] * shdx[l+i*LX];
1169 rww[l] += rwt * shdz[k+l*LX];
1170 wwijke += shws[sh + i+l*LX] * shdy[l + j*LX];
1171 }
1172 ruw[k] += uwijke;
1173 rvw[k] += vwijke;
1174 rww[k] += wwijke;
1175 }
1176 if (active) {
1177#pragma unroll
1178 for (int k = 0; k < LX; ++k){
1179 au[ij + k*LX*LX + ele] = ruw[k];
1180 av[ij + k*LX*LX + ele] = rvw[k];
1181 aw[ij + k*LX*LX + ele] = rww[k];
1182 }
1183 }
1184}
1185
1186template< typename T, const int LX, const int EB >
1189 T * __restrict__ av,
1190 T * __restrict__ aw,
1191 const T * __restrict__ u,
1192 const T * __restrict__ v,
1193 const T * __restrict__ w,
1194 const T * __restrict__ dx,
1195 const T * __restrict__ dy,
1196 const T * __restrict__ dz,
1197 const T * __restrict__ h1,
1198 const T * __restrict__ g11,
1199 const T * __restrict__ g22,
1200 const T * __restrict__ g33,
1201 const T * __restrict__ g12,
1202 const T * __restrict__ g13,
1203 const T * __restrict__ g23,
1204 const int * __restrict__ ind,
1205 const int nelv) {
1206
1207 /* Element independent, one copy per block */
1208 __shared__ T shdx[LX * (LX+1)];
1209 __shared__ T shdy[LX * (LX+1)];
1210 __shared__ T shdz[LX * (LX+1)];
1211
1212 /* One slice per element in the block */
1213 __shared__ T shu[EB * LX * (LX+1)];
1214 __shared__ T shur[EB * LX * LX];
1215 __shared__ T shus[EB * LX * (LX+1)];
1216
1217 __shared__ T shv[EB * LX * (LX+1)];
1218 __shared__ T shvr[EB * LX * LX];
1219 __shared__ T shvs[EB * LX * (LX+1)];
1220
1221 __shared__ T shw[EB * LX * (LX+1)];
1222 __shared__ T shwr[EB * LX * LX];
1223 __shared__ T shws[EB * LX * (LX+1)];
1224
1225 static_assert(sizeof(shdx) +
1226 sizeof(shdy) +
1227 sizeof(shdz) +
1228 sizeof(shu) +
1229 sizeof(shur) +
1230 sizeof(shus) +
1231 sizeof(shv) +
1232 sizeof(shvr) +
1233 sizeof(shvs) +
1234 sizeof(shw) +
1235 sizeof(shwr) +
1236 sizeof(shws)
1238 "kstep block exceeds the shared memory budget");
1239
1240 T ru[LX];
1241 T rv[LX];
1242 T rw[LX];
1243
1244 T ruw[LX];
1245 T rvw[LX];
1246 T rww[LX];
1247
1248 T rut;
1249 T rvt;
1250 T rwt;
1251
1252 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
1253 constant folded away and the kernel is exactly what it was before */
1254 const int eb = (EB == 1) ? 0 : threadIdx.z;
1255 const int e_blk = blockIdx.x * EB + eb;
1256 const bool active = (EB == 1) ? true : (e_blk < nelv);
1257 const int e = active ? e_blk : (nelv - 1);
1258 const int j = threadIdx.y;
1259 const int i = threadIdx.x;
1260 const int ij = i + j*LX;
1261 const int ij_p = i + j*(LX+1);
1262 const int sh = eb*LX*LX;
1263 const int sh_p = eb*LX*(LX+1);
1264 const int ele = e*LX*LX*LX;
1265 const int gele = (ind[e] - 1)*LX*LX*LX;
1266
1267 if (eb == 0) {
1268 shdx[ij_p] = dx[ij];
1269 shdy[ij_p] = dy[ij];
1270 shdz[ij_p] = dz[ij];
1271 }
1272
1273#pragma unroll
1274 for(int k = 0; k < LX; ++k){
1275 ru[k] = u[ij + k*LX*LX + ele];
1276 ruw[k] = 0.0;
1277
1278 rv[k] = v[ij + k*LX*LX + ele];
1279 rvw[k] = 0.0;
1280
1281 rw[k] = w[ij + k*LX*LX + ele];
1282 rww[k] = 0.0;
1283 }
1284
1285
1286 __syncthreads();
1287#pragma unroll
1288 for (int k = 0; k < LX; ++k){
1289 const int ijk = ij + k*LX*LX;
1290 const T G00 = g11[ijk+gele];
1291 const T G11 = g22[ijk+gele];
1292 const T G22 = g33[ijk+gele];
1293 const T G01 = g12[ijk+gele];
1294 const T G02 = g13[ijk+gele];
1295 const T G12 = g23[ijk+gele];
1296 const T H1 = h1[ijk+ele];
1297 T uttmp = 0.0;
1298 T vttmp = 0.0;
1299 T wttmp = 0.0;
1300 shu[sh_p + ij_p] = ru[k];
1301 shv[sh_p + ij_p] = rv[k];
1302 shw[sh_p + ij_p] = rw[k];
1303#pragma unroll
1304 for (int l = 0; l < LX; l++){
1305 uttmp += shdz[k+l*(LX+1)] * ru[l];
1306 vttmp += shdz[k+l*(LX+1)] * rv[l];
1307 wttmp += shdz[k+l*(LX+1)] * rw[l];
1308 }
1309 __syncthreads();
1310
1311 T urtmp = 0.0;
1312 T ustmp = 0.0;
1313
1314 T vrtmp = 0.0;
1315 T vstmp = 0.0;
1316
1317 T wrtmp = 0.0;
1318 T wstmp = 0.0;
1319#pragma unroll
1320 for (int l = 0; l < LX; l++){
1321 urtmp += shdx[i+l*(LX+1)] * shu[sh_p + l+j*(LX+1)];
1322 ustmp += shdy[j+l*(LX+1)] * shu[sh_p + i+l*(LX+1)];
1323
1324 vrtmp += shdx[i+l*(LX+1)] * shv[sh_p + l+j*(LX+1)];
1325 vstmp += shdy[j+l*(LX+1)] * shv[sh_p + i+l*(LX+1)];
1326
1327 wrtmp += shdx[i+l*(LX+1)] * shw[sh_p + l+j*(LX+1)];
1328 wstmp += shdy[j+l*(LX+1)] * shw[sh_p + i+l*(LX+1)];
1329 }
1330
1331 shur[sh + ij] = H1
1332 * (G00 * urtmp
1333 + G01 * ustmp
1334 + G02 * uttmp);
1335 shus[sh_p + ij_p] = H1
1336 * (G01 * urtmp
1337 + G11 * ustmp
1338 + G12 * uttmp);
1339 rut = H1
1340 * (G02 * urtmp
1341 + G12 * ustmp
1342 + G22 * uttmp);
1343
1344 shvr[sh + ij] = H1
1345 * (G00 * vrtmp
1346 + G01 * vstmp
1347 + G02 * vttmp);
1348 shvs[sh_p + ij_p] = H1
1349 * (G01 * vrtmp
1350 + G11 * vstmp
1351 + G12 * vttmp);
1352 rvt = H1
1353 * (G02 * vrtmp
1354 + G12 * vstmp
1355 + G22 * vttmp);
1356
1357 shwr[sh + ij] = H1
1358 * (G00 * wrtmp
1359 + G01 * wstmp
1360 + G02 * wttmp);
1361 shws[sh_p + ij_p] = H1
1362 * (G01 * wrtmp
1363 + G11 * wstmp
1364 + G12 * wttmp);
1365 rwt = H1
1366 * (G02 * wrtmp
1367 + G12 * wstmp
1368 + G22 * wttmp);
1369
1370 __syncthreads();
1371
1372 T uwijke = 0.0;
1373 T vwijke = 0.0;
1374 T wwijke = 0.0;
1375#pragma unroll
1376 for (int l = 0; l < LX; l++){
1377 uwijke += shur[sh + l+j*LX] * shdx[l+i*(LX+1)];
1378 ruw[l] += rut * shdz[k+l*(LX+1)];
1379 uwijke += shus[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1380
1381 vwijke += shvr[sh + l+j*LX] * shdx[l+i*(LX+1)];
1382 rvw[l] += rvt * shdz[k+l*(LX+1)];
1383 vwijke += shvs[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1384
1385 wwijke += shwr[sh + l+j*LX] * shdx[l+i*(LX+1)];
1386 rww[l] += rwt * shdz[k+l*(LX+1)];
1387 wwijke += shws[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1388 }
1389 ruw[k] += uwijke;
1390 rvw[k] += vwijke;
1391 rww[k] += wwijke;
1392 }
1393 if (active) {
1394#pragma unroll
1395 for (int k = 0; k < LX; ++k){
1396 au[ij + k*LX*LX + ele] = ruw[k];
1397 av[ij + k*LX*LX + ele] = rvw[k];
1398 aw[ij + k*LX*LX + ele] = rww[k];
1399 }
1400 }
1401}
1402
1403
1430#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 800) && (__CUDA_ARCH__ < 1000)
1431
1432template< const int LX, const int NW >
1435 double * __restrict__ av,
1436 double * __restrict__ aw,
1437 const double * __restrict__ u,
1438 const double * __restrict__ v,
1439 const double * __restrict__ w,
1440 const double * __restrict__ dx,
1441 const double * __restrict__ dy,
1442 const double * __restrict__ dz,
1443 const double * __restrict__ h1,
1444 const double * __restrict__ g11,
1445 const double * __restrict__ g22,
1446 const double * __restrict__ g33,
1447 const double * __restrict__ g12,
1448 const double * __restrict__ g13,
1449 const double * __restrict__ g23,
1450 const int * __restrict__ ind) {
1451
1452 /* Element independent, one copy per block */
1454 __shared__ __align__(16) double shdy[DMMA_MAT];
1455 __shared__ __align__(16) double shdz[DMMA_MAT];
1456
1457 /* One component at a time: shc carries it in and the result out, shr, shs
1458 and sht its reference derivatives */
1459 __shared__ __align__(16) double shc[DMMA_CUBE];
1460 __shared__ __align__(16) double shr[DMMA_CUBE];
1461 __shared__ __align__(16) double shs[DMMA_CUBE];
1462 __shared__ __align__(16) double sht[DMMA_CUBE];
1463
1465 sizeof(shdy) +
1466 sizeof(shdz) +
1467 sizeof(shc) +
1468 sizeof(shr) +
1469 sizeof(shs) +
1470 sizeof(sht)
1473
1474 enum { NTHRDS = 32 * NW,
1475 LX3 = LX * LX * LX,
1476 PPT = (LX3 + NTHRDS - 1) / NTHRDS };
1477
1478 const int tid = threadIdx.x;
1479 const int wf = tid >> 5;
1480 const int ele = blockIdx.x * LX3;
1481 const int gele = (ind[blockIdx.x] - 1) * LX3;
1482
1483 /* The geometric factors, read once and reused by all three components */
1484 double rG00[PPT], rG11[PPT], rG22[PPT];
1485 double rG01[PPT], rG02[PPT], rG12[PPT];
1486 double rH1[PPT];
1487 int rc[PPT];
1488
1489 /* The padding only has to be finite, see the note above. At LX == DMMA_P
1490 there is none and this is folded away */
1491 if (LX < DMMA_P) {
1492 for (int p = tid; p < DMMA_MAT; p += NTHRDS) {
1493 shdx[p] = 0.0;
1494 shdy[p] = 0.0;
1495 shdz[p] = 0.0;
1496 }
1497 for (int p = tid; p < DMMA_CUBE; p += NTHRDS) {
1498 shc[p] = 0.0;
1499 }
1500 __syncthreads();
1501 }
1502
1503 for (int p = tid; p < LX * LX; p += NTHRDS) {
1504 const int i = p % LX;
1505 const int l = p / LX;
1506 const int m = i + DMMA_P * l;
1507 shdx[m] = dx[p];
1508 shdy[m] = dy[p];
1509 shdz[m] = dz[p];
1510 }
1511
1512#pragma unroll
1513 for (int q = 0; q < PPT; q++) {
1514 const int p = tid + q * NTHRDS;
1515
1516 if (p < LX3) {
1517 const int i = p % LX;
1518 const int jk = p / LX;
1519 const int j = jk % LX;
1520 const int k = jk / LX;
1521 rc[q] = i + DMMA_SI * j + DMMA_SJ * k;
1522 rG00[q] = g11[p + gele];
1523 rG11[q] = g22[p + gele];
1524 rG22[q] = g33[p + gele];
1525 rG01[q] = g12[p + gele];
1526 rG02[q] = g13[p + gele];
1527 rG12[q] = g23[p + gele];
1528 rH1[q] = h1[p + ele];
1529 } else {
1530 rc[q] = 0;
1531 rG00[q] = 0.0;
1532 rG11[q] = 0.0;
1533 rG22[q] = 0.0;
1534 rG01[q] = 0.0;
1535 rG02[q] = 0.0;
1536 rG12[q] = 0.0;
1537 rH1[q] = 0.0;
1538 }
1539 }
1540
1541 __syncthreads();
1542
1543 const double * const cin[3] = { u, v, w };
1544 double * const cout[3] = { au, av, aw };
1545
1546#pragma unroll
1547 for (int c = 0; c < 3; c++) {
1548
1549 for (int p = tid; p < LX3; p += NTHRDS) {
1550 const int i = p % LX;
1551 const int jk = p / LX;
1552 const int j = jk % LX;
1553 const int k = jk / LX;
1554 shc[i + DMMA_SI * j + DMMA_SJ * k] = cin[c][p + ele];
1555 }
1556
1557 __syncthreads();
1558
1562
1563 __syncthreads();
1564
1565#pragma unroll
1566 for (int q = 0; q < PPT; q++) {
1567 const int p = tid + q * NTHRDS;
1568
1569 if (p < LX3) {
1570 const int idx = rc[q];
1571 const double rtmp = shr[idx];
1572 const double stmp = shs[idx];
1573 const double ttmp = sht[idx];
1574
1575 shr[idx] = rH1[q]
1576 * (rG00[q] * rtmp
1577 + rG01[q] * stmp
1578 + rG02[q] * ttmp);
1579 shs[idx] = rH1[q]
1580 * (rG01[q] * rtmp
1581 + rG11[q] * stmp
1582 + rG12[q] * ttmp);
1583 sht[idx] = rH1[q]
1584 * (rG02[q] * rtmp
1585 + rG12[q] * stmp
1586 + rG22[q] * ttmp);
1587 }
1588 }
1589
1590 __syncthreads();
1591
1593 __syncthreads();
1595 __syncthreads();
1597 __syncthreads();
1598
1599 for (int p = tid; p < LX3; p += NTHRDS) {
1600 const int i = p % LX;
1601 const int jk = p / LX;
1602 const int j = jk % LX;
1603 const int k = jk / LX;
1604 cout[c][p + ele] = shc[i + DMMA_SI * j + DMMA_SJ * k];
1605 }
1606
1607 /* shc is restaged by the next component */
1608 __syncthreads();
1609 }
1610}
1611
1612#endif // __CUDA_ARCH__ in [800, 1000)
1613
1614/*
1615 * Compile-time dispatch onto the vector DMMA element kernel, see the note on
1616 * ax_helm_compr_dmma_dispatch above.
1617 */
1618template< typename T, const int LX, const int NW >
1621 T * __restrict__,
1622 T * __restrict__,
1623 const T * __restrict__,
1624 const T * __restrict__,
1625 const T * __restrict__,
1626 const T * __restrict__,
1627 const T * __restrict__,
1628 const T * __restrict__,
1629 const T * __restrict__,
1630 const T * __restrict__,
1631 const T * __restrict__,
1632 const T * __restrict__,
1633 const T * __restrict__,
1634 const T * __restrict__,
1635 const T * __restrict__,
1636 const int * __restrict__) { }
1637};
1638
1639#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 800) && (__CUDA_ARCH__ < 1000)
1640
1641/* Keep in sync with dmma_vector_lx_supported() in dmma_kernel.h, which is
1642 4 <= LX <= 8 and not the 2 <= LX <= 8 of the packed scalar kernel */
1643#define NEKO_AX_HELM_COMPR_DMMA_VECTOR_DISPATCH(LXV) \
1644 template< const int NW > \
1645 struct ax_helm_compr_dmma_vector_dispatch< double, LXV, NW > { \
1646 __device__ static void run(double * __restrict__ au, \
1647 double * __restrict__ av, \
1648 double * __restrict__ aw, \
1649 const double * __restrict__ u, \
1650 const double * __restrict__ v, \
1651 const double * __restrict__ w, \
1652 const double * __restrict__ dx, \
1653 const double * __restrict__ dy, \
1654 const double * __restrict__ dz, \
1655 const double * __restrict__ h1, \
1656 const double * __restrict__ g11, \
1657 const double * __restrict__ g22, \
1658 const double * __restrict__ g33, \
1659 const double * __restrict__ g12, \
1660 const double * __restrict__ g13, \
1661 const double * __restrict__ g23, \
1662 const int * __restrict__ ind) { \
1663 ax_helm_compr_dmma_vector_elem< LXV, NW >(au, av, aw, u, v, w, dx, dy, \
1664 dz, h1, g11, g22, g33, g12, \
1665 g13, g23, ind); \
1666 } \
1667 }
1668
1674
1675#endif // __CUDA_ARCH__ in [800, 1000)
1676
1677template< typename T, const int LX, const int NW >
1678__global__ void NEKO_EB_BOUNDS(32 * NW)
1680 T * __restrict__ av,
1681 T * __restrict__ aw,
1682 const T * __restrict__ u,
1683 const T * __restrict__ v,
1684 const T * __restrict__ w,
1685 const T * __restrict__ dx,
1686 const T * __restrict__ dy,
1687 const T * __restrict__ dz,
1688 const T * __restrict__ h1,
1689 const T * __restrict__ g11,
1690 const T * __restrict__ g22,
1691 const T * __restrict__ g33,
1692 const T * __restrict__ g12,
1693 const T * __restrict__ g13,
1694 const T * __restrict__ g23,
1695 const int * __restrict__ ind) {
1696
1698 dx, dy, dz, h1,
1699 g11, g22, g33,
1700 g12, g13, g23, ind);
1701}
1702
1733#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) && \
1734 (__CUDA_ARCH__ < 1000) && NEKO_TMA_TOOLKIT
1735
1736template< const int LX, const int NW >
1739 double * __restrict__ av,
1740 double * __restrict__ aw,
1741 const double * __restrict__ u,
1742 const double * __restrict__ v,
1743 const double * __restrict__ w,
1744 const double * __restrict__ dx,
1745 const double * __restrict__ dy,
1746 const double * __restrict__ dz,
1747 const double * __restrict__ h1,
1748 const double * __restrict__ g11,
1749 const double * __restrict__ g22,
1750 const double * __restrict__ g33,
1751 const double * __restrict__ g12,
1752 const double * __restrict__ g13,
1753 const double * __restrict__ g23,
1754 const int * __restrict__ ind) {
1755
1756 /* Element independent, one copy per block */
1758 __shared__ __align__(128) double shdy[DMMA_MAT];
1759 __shared__ __align__(128) double shdz[DMMA_MAT];
1760
1761 /* One component at a time: shc carries it in and the result out, shr, shs
1762 and sht its reference derivatives */
1763 __shared__ __align__(128) double shc[DMMA_CUBE];
1764 __shared__ __align__(128) double shr[DMMA_CUBE];
1765 __shared__ __align__(128) double shs[DMMA_CUBE];
1766 __shared__ __align__(128) double sht[DMMA_CUBE];
1767
1768 /* h1, g11, g22, g33, g12, g13, g23, shared by all three components */
1770
1771 __shared__ __align__(8) unsigned long long bar_c;
1772 __shared__ __align__(8) unsigned long long bar_g;
1773
1775 sizeof(shdy) +
1776 sizeof(shdz) +
1777 sizeof(shc) +
1778 sizeof(shr) +
1779 sizeof(shs) +
1780 sizeof(sht) +
1781 sizeof(shg) +
1782 sizeof(bar_c) +
1783 sizeof(bar_g)
1786
1787 /* Keep in step with dmma_tma_vector_lx_supported() */
1790
1791 enum { CUBE_BYTES = DMMA_CUBE * (int) sizeof(double) };
1792
1793 const int nthrds = 32 * NW;
1794 const int tid = threadIdx.x;
1795 const int wf = tid >> 5;
1796 const int ele = blockIdx.x * DMMA_CUBE;
1797 const int gele = (ind[blockIdx.x] - 1) * DMMA_CUBE;
1798
1799 if (tid == 0) {
1801 tma_barrier_init(&bar_g, 1);
1802 }
1803
1804 /* At LX == DMMA_P the derivative matrix fills the staged one exactly, and
1805 there is no padding anywhere to zero */
1806 for (int p = tid; p < DMMA_MAT; p += nthrds) {
1807 shdx[p] = dx[p];
1808 shdy[p] = dy[p];
1809 shdz[p] = dz[p];
1810 }
1811
1812 /* Both barriers initialised and both derivative matrices staged before
1813 anyone waits on the one or contracts with the other */
1814 __syncthreads();
1815
1816 /* Read once, outside the component loop, and waited on inside it at the
1817 first step that needs them */
1818 if (tid == 0) {
1819 tma_expect(&bar_g, DMMA_NG * CUBE_BYTES);
1820 tma_load(shg[0], h1 + ele, CUBE_BYTES, &bar_g);
1821 tma_load(shg[1], g11 + gele, CUBE_BYTES, &bar_g);
1822 tma_load(shg[2], g22 + gele, CUBE_BYTES, &bar_g);
1823 tma_load(shg[3], g33 + gele, CUBE_BYTES, &bar_g);
1824 tma_load(shg[4], g12 + gele, CUBE_BYTES, &bar_g);
1825 tma_load(shg[5], g13 + gele, CUBE_BYTES, &bar_g);
1826 tma_load(shg[6], g23 + gele, CUBE_BYTES, &bar_g);
1827 }
1828
1829 const double * const cin[3] = { u, v, w };
1830 double * const cout[3] = { au, av, aw };
1831
1832#pragma unroll
1833 for (int c = 0; c < 3; c++) {
1834
1835 if (tid == 0) {
1837 tma_load(shc, cin[c] + ele, CUBE_BYTES, &bar_c);
1838 }
1839
1840 /* bar_c is re-armed by every completion, so the parity of the phase each
1841 component's copy completes alternates. The __syncthreads() at the foot
1842 of the loop is what keeps the next component's arrive from racing a
1843 thread that has not yet observed this one */
1844 tma_wait(&bar_c, c & 1);
1845
1849
1850 __syncthreads();
1851
1852 /* Resident from here on; only the first component ever waits */
1853 if (c == 0) {
1854 tma_wait(&bar_g, 0);
1855 }
1856
1857 for (int p = tid; p < DMMA_CUBE; p += nthrds) {
1858 const double H1 = shg[0][p];
1859 const double G00 = shg[1][p];
1860 const double G11 = shg[2][p];
1861 const double G22 = shg[3][p];
1862 const double G01 = shg[4][p];
1863 const double G02 = shg[5][p];
1864 const double G12 = shg[6][p];
1865
1866 const double rtmp = shr[p];
1867 const double stmp = shs[p];
1868 const double ttmp = sht[p];
1869
1870 shr[p] = H1
1871 * (G00 * rtmp
1872 + G01 * stmp
1873 + G02 * ttmp);
1874 shs[p] = H1
1875 * (G01 * rtmp
1876 + G11 * stmp
1877 + G12 * ttmp);
1878 sht[p] = H1
1879 * (G02 * rtmp
1880 + G12 * stmp
1881 + G22 * ttmp);
1882 }
1883
1884 __syncthreads();
1885
1887 __syncthreads();
1889 __syncthreads();
1891
1892 /* The contractions wrote shc through the generic proxy and the bulk store
1893 reads it through the async one; see tma_fence_shared() */
1895 __syncthreads();
1896
1897 if (tid == 0) {
1900 }
1901
1902 /* shc is restaged by the next component, and nothing may retire while the
1903 store is still reading it */
1904 __syncthreads();
1905 }
1906}
1907
1908#endif // __CUDA_ARCH__ == sm_90 with a CUDA 12 toolkit
1909
1910/*
1911 * Compile-time dispatch onto the TMA staged vector DMMA element kernel, see
1912 * the note on ax_helm_compr_dmma_tma_dispatch above.
1913 */
1914template< typename T, const int LX, const int NW >
1917 T * __restrict__,
1918 T * __restrict__,
1919 const T * __restrict__,
1920 const T * __restrict__,
1921 const T * __restrict__,
1922 const T * __restrict__,
1923 const T * __restrict__,
1924 const T * __restrict__,
1925 const T * __restrict__,
1926 const T * __restrict__,
1927 const T * __restrict__,
1928 const T * __restrict__,
1929 const T * __restrict__,
1930 const T * __restrict__,
1931 const T * __restrict__,
1932 const int * __restrict__) { }
1933};
1934
1935#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) && \
1936 (__CUDA_ARCH__ < 1000) && NEKO_TMA_TOOLKIT
1937
1938/* Keep in sync with dmma_tma_vector_lx_supported() in dmma_tma_kernel.h */
1939#define NEKO_AX_HELM_COMPR_DMMA_TMA_VECTOR_DISPATCH(LXV) \
1940 template< const int NW > \
1941 struct ax_helm_compr_dmma_tma_vector_dispatch< double, LXV, NW > { \
1942 __device__ static void run(double * __restrict__ au, \
1943 double * __restrict__ av, \
1944 double * __restrict__ aw, \
1945 const double * __restrict__ u, \
1946 const double * __restrict__ v, \
1947 const double * __restrict__ w, \
1948 const double * __restrict__ dx, \
1949 const double * __restrict__ dy, \
1950 const double * __restrict__ dz, \
1951 const double * __restrict__ h1, \
1952 const double * __restrict__ g11, \
1953 const double * __restrict__ g22, \
1954 const double * __restrict__ g33, \
1955 const double * __restrict__ g12, \
1956 const double * __restrict__ g13, \
1957 const double * __restrict__ g23, \
1958 const int * __restrict__ ind) { \
1959 ax_helm_compr_dmma_tma_vector_elem< LXV, NW >(au, av, aw, u, v, w, \
1960 dx, dy, dz, h1, \
1961 g11, g22, g33, \
1962 g12, g13, g23, ind); \
1963 } \
1964 }
1965
1967
1968#endif // __CUDA_ARCH__ == sm_90 with a CUDA 12 toolkit
1969
1970template< typename T, const int LX, const int NW >
1971__global__ void NEKO_EB_BOUNDS(32 * NW)
1973 T * __restrict__ av,
1974 T * __restrict__ aw,
1975 const T * __restrict__ u,
1976 const T * __restrict__ v,
1977 const T * __restrict__ w,
1978 const T * __restrict__ dx,
1979 const T * __restrict__ dy,
1980 const T * __restrict__ dz,
1981 const T * __restrict__ h1,
1982 const T * __restrict__ g11,
1983 const T * __restrict__ g22,
1984 const T * __restrict__ g33,
1985 const T * __restrict__ g12,
1986 const T * __restrict__ g13,
1987 const T * __restrict__ g23,
1988 const int * __restrict__ ind) {
1989
1991 dx, dy, dz, h1,
1992 g11, g22, g33,
1993 g12, g13, g23, ind);
1994}
1995
1996
2030#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) && \
2031 (__CUDA_ARCH__ < 1000) && NEKO_TMA_TOOLKIT
2032
2033template< const int LX, const int NW >
2036 double * __restrict__ av,
2037 double * __restrict__ aw,
2038 const double * __restrict__ u,
2039 const double * __restrict__ v,
2040 const double * __restrict__ w,
2041 const double * __restrict__ dx,
2042 const double * __restrict__ dy,
2043 const double * __restrict__ dz,
2044 const double * __restrict__ h1,
2045 const double * __restrict__ g11,
2046 const double * __restrict__ g22,
2047 const double * __restrict__ g33,
2048 const double * __restrict__ g12,
2049 const double * __restrict__ g13,
2050 const double * __restrict__ g23,
2051 const int * __restrict__ ind) {
2052
2053 /* Keep in step with dmma_tma_batch_lx_supported() */
2054 static_assert(LX == DMMA_P,
2055 "the dmma tma batch variant stages whole cubes only");
2056
2060
2061 enum { CUBE_BYTES = DMMA_CUBE * (int) sizeof(double),
2062 /* v, w and the seven factors -- everything but the first cube */
2063 REST_BYTES = (DMMA_NG + 2) * CUBE_BYTES };
2064
2065 const int nthrds = 32 * NW;
2066 const int tid = threadIdx.x;
2067 const int wf = tid >> 5;
2068 const int ele = blockIdx.x * DMMA_CUBE;
2069 const int gele = (ind[blockIdx.x] - 1) * DMMA_CUBE;
2070
2071 if (tid == 0) {
2072 tma_barrier_init(&sm.bar_a, 1);
2073 tma_barrier_init(&sm.bar_b, 1);
2074 }
2075
2076 /* At LX == DMMA_P the derivative matrix fills the staged one exactly, and
2077 there is no padding anywhere to zero */
2078 for (int p = tid; p < DMMA_MAT; p += nthrds) {
2079 sm.dx[p] = dx[p];
2080 sm.dy[p] = dy[p];
2081 sm.dz[p] = dz[p];
2082 }
2083
2084 /* Both barriers initialised and both derivative matrices staged before
2085 anyone waits on the one or contracts with the other */
2086 __syncthreads();
2087
2088 /* The whole element, ten copies, one batch. u leads because it is the only
2089 one anything waits for before the contractions start */
2090 if (tid == 0) {
2091 tma_expect(&sm.bar_a, CUBE_BYTES);
2092 tma_load(sm.c[0], u + ele, CUBE_BYTES, &sm.bar_a);
2093
2094 tma_expect(&sm.bar_b, REST_BYTES);
2095 tma_load(sm.c[1], v + ele, CUBE_BYTES, &sm.bar_b);
2096 tma_load(sm.c[2], w + ele, CUBE_BYTES, &sm.bar_b);
2097 tma_load(sm.g[0], h1 + ele, CUBE_BYTES, &sm.bar_b);
2098 tma_load(sm.g[1], g11 + gele, CUBE_BYTES, &sm.bar_b);
2099 tma_load(sm.g[2], g22 + gele, CUBE_BYTES, &sm.bar_b);
2100 tma_load(sm.g[3], g33 + gele, CUBE_BYTES, &sm.bar_b);
2101 tma_load(sm.g[4], g12 + gele, CUBE_BYTES, &sm.bar_b);
2102 tma_load(sm.g[5], g13 + gele, CUBE_BYTES, &sm.bar_b);
2103 tma_load(sm.g[6], g23 + gele, CUBE_BYTES, &sm.bar_b);
2104 }
2105
2106 /* The first component only. The other nine copies are still arriving */
2107 tma_wait(&sm.bar_a, 0);
2108
2109 double * const cout[3] = { au, av, aw };
2110
2111#pragma unroll
2112 for (int c = 0; c < 3; c++) {
2113
2117
2118 __syncthreads();
2119
2120 /* Everything else has landed by here; components 1 and 2 never wait */
2121 if (c == 0) {
2122 tma_wait(&sm.bar_b, 0);
2123 }
2124
2125 for (int p = tid; p < DMMA_CUBE; p += nthrds) {
2126 const double H1 = sm.g[0][p];
2127 const double G00 = sm.g[1][p];
2128 const double G11 = sm.g[2][p];
2129 const double G22 = sm.g[3][p];
2130 const double G01 = sm.g[4][p];
2131 const double G02 = sm.g[5][p];
2132 const double G12 = sm.g[6][p];
2133
2134 const double rtmp = sm.r[p];
2135 const double stmp = sm.s[p];
2136 const double ttmp = sm.t[p];
2137
2138 sm.r[p] = H1
2139 * (G00 * rtmp
2140 + G01 * stmp
2141 + G02 * ttmp);
2142 sm.s[p] = H1
2143 * (G01 * rtmp
2144 + G11 * stmp
2145 + G12 * ttmp);
2146 sm.t[p] = H1
2147 * (G02 * rtmp
2148 + G12 * stmp
2149 + G22 * ttmp);
2150 }
2151
2152 __syncthreads();
2153
2154 /* The result overwrites the component's own staged input, which nothing
2155 reads again */
2157 __syncthreads();
2158 dmma_contract< 1, true, true, NW >(sm.c[c], sm.dy, sm.s, wf);
2159 __syncthreads();
2160 dmma_contract< 2, true, true, NW >(sm.c[c], sm.dz, sm.t, wf);
2161
2162 /* The contractions wrote sm.c[c] through the generic proxy and the bulk
2163 store reads it through the async one; see tma_fence_shared(). The
2164 barrier that follows also separates this component's last reads of
2165 r, s and t from the next one's writes to them */
2167 __syncthreads();
2168
2169 /* Issued and left uncommitted: the next component reads its own cube, so
2170 nothing here waits on this store. All three are committed together
2171 below */
2172 if (tid == 0) {
2173 tma_store(cout[c] + ele, sm.c[c], CUBE_BYTES);
2174 }
2175 }
2176
2177 /* Commit the three stores as one group and wait for them to have read their
2178 cubes out; shared memory lives only as long as the block does */
2179 if (tid == 0) {
2181 }
2182
2183 __syncthreads();
2184}
2185
2186#endif // __CUDA_ARCH__ == sm_90 with a CUDA 12 toolkit
2187
2188/*
2189 * Compile-time dispatch onto the batched TMA element kernel, see the note on
2190 * ax_helm_compr_dmma_tma_dispatch above.
2191 */
2192template< typename T, const int LX, const int NW >
2195 T * __restrict__,
2196 T * __restrict__,
2197 const T * __restrict__,
2198 const T * __restrict__,
2199 const T * __restrict__,
2200 const T * __restrict__,
2201 const T * __restrict__,
2202 const T * __restrict__,
2203 const T * __restrict__,
2204 const T * __restrict__,
2205 const T * __restrict__,
2206 const T * __restrict__,
2207 const T * __restrict__,
2208 const T * __restrict__,
2209 const T * __restrict__,
2210 const int * __restrict__) { }
2211};
2212
2213#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900) && \
2214 (__CUDA_ARCH__ < 1000) && NEKO_TMA_TOOLKIT
2215
2216/* Keep in sync with dmma_tma_batch_lx_supported() in dmma_tma_kernel.h */
2217#define NEKO_AX_HELM_COMPR_DMMA_TMA_BATCH_DISPATCH(LXV) \
2218 template< const int NW > \
2219 struct ax_helm_compr_dmma_tma_batch_dispatch< double, LXV, NW > { \
2220 __device__ static void run(double * __restrict__ au, \
2221 double * __restrict__ av, \
2222 double * __restrict__ aw, \
2223 const double * __restrict__ u, \
2224 const double * __restrict__ v, \
2225 const double * __restrict__ w, \
2226 const double * __restrict__ dx, \
2227 const double * __restrict__ dy, \
2228 const double * __restrict__ dz, \
2229 const double * __restrict__ h1, \
2230 const double * __restrict__ g11, \
2231 const double * __restrict__ g22, \
2232 const double * __restrict__ g33, \
2233 const double * __restrict__ g12, \
2234 const double * __restrict__ g13, \
2235 const double * __restrict__ g23, \
2236 const int * __restrict__ ind) { \
2237 ax_helm_compr_dmma_tma_batch_elem< LXV, NW >(au, av, aw, u, v, w, \
2238 dx, dy, dz, h1, \
2239 g11, g22, g33, \
2240 g12, g13, g23, ind); \
2241 } \
2242 }
2243
2245
2246#endif // __CUDA_ARCH__ == sm_90 with a CUDA 12 toolkit
2247
2248template< typename T, const int LX, const int NW >
2249__global__ void NEKO_EB_BOUNDS(32 * NW)
2251 T * __restrict__ av,
2252 T * __restrict__ aw,
2253 const T * __restrict__ u,
2254 const T * __restrict__ v,
2255 const T * __restrict__ w,
2256 const T * __restrict__ dx,
2257 const T * __restrict__ dy,
2258 const T * __restrict__ dz,
2259 const T * __restrict__ h1,
2260 const T * __restrict__ g11,
2261 const T * __restrict__ g22,
2262 const T * __restrict__ g33,
2263 const T * __restrict__ g12,
2264 const T * __restrict__ g13,
2265 const T * __restrict__ g23,
2266 const int * __restrict__ ind) {
2267
2269 dx, dy, dz, h1,
2270 g11, g22, g33,
2271 g12, g13, g23, ind);
2272}
2273
2274/*
2275 * Opt into the batched variant's dynamic allocation, once per specialisation.
2276 *
2277 * A block gets 48 kB of shared memory without asking; anything past that has
2278 * to be requested per kernel, and the carveout moved with it or the extra is
2279 * granted at the expense of nothing. Both are properties of the function, not
2280 * of the launch, so this is a function local static -- one flag per
2281 * <T, LX, NW> -- and the launch macro calls it ahead of every launch, where
2282 * after the first it is a single predictable branch.
2283 *
2284 * Returns false if the device refuses, which the tuner has already ruled out
2285 * via cuda_have_tma_batch(); the error is cleared rather than left to surface
2286 * against an unrelated CUDA_CHECK later.
2287 */
2288template< typename T, const int LX, const int NW >
2290{
2291 static int state = -1;
2292
2293 if (state < 0) {
2294 const void * const fn =
2299
2300 if (err == cudaSuccess) {
2304 }
2305 state = (err == cudaSuccess) ? 1 : 0;
2306 if (state == 0) {
2308 }
2309 }
2310 return state == 1;
2311}
2312
2313#endif // __MATH_AX_HELM_COMPR_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
__shared__ T shv[EB *LX *LX]
const int ij_p
__shared__ T shdy[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 T *__restrict__ const T *__restrict__ const T *__restrict__ g23
T rvw[LX]
__global__ void T *__restrict__ T *__restrict__ aw
__shared__ T shu[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__ g22
const int sh
const int eb
__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 T *__restrict__ const T *__restrict__ const T *__restrict__ const int *__restrict__ ind
__shared__ T shdz[LX *LX]
T rww[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 T *__restrict__ const T *__restrict__ g13
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ w
__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 T *__restrict__ const T *__restrict__ const T *__restrict__ const int *__restrict__ const int nelv
const int sh_p
const int i
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__ const T *__restrict__ const T *__restrict__ g12
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dz
__global__ void const T *__restrict__ const T *__restrict__ dx
__global__ void const T *__restrict__ u
__shared__ T shus[EB *LX *LX]
const int e
__global__ void T *__restrict__ av
__shared__ T shur[EB *LX *LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ dy
T ruw[LX]
__shared__ T shvs[EB *LX *LX]
const int e_blk
const int gele
T ru[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__ g33
__shared__ T shw[EB *LX *LX]
const int ele
__shared__ T shwr[EB *LX *LX]
__global__ void ax_helm_compr_kernel_1d(T *__restrict__ w, const T *__restrict__ u, const T *__restrict__ dx, const T *__restrict__ dy, const T *__restrict__ dz, const T *__restrict__ dxt, const T *__restrict__ dyt, const T *__restrict__ dzt, const T *__restrict__ h1, const T *__restrict__ g11, const T *__restrict__ g22, const T *__restrict__ g33, const T *__restrict__ g12, const T *__restrict__ g13, const T *__restrict__ g23, const int *__restrict__ ind)
T rw[LX]
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ v
const int j
__shared__ T shvr[EB *LX *LX]
__syncthreads()
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g11
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ h1
__shared__ T shws[EB *LX *LX]
static bool ax_helm_compr_dmma_tma_batch_optin()
shdx[ij]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dyt
__shared__ T shdzt[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__ dzt
__global__ void const T *__restrict__ x
__shared__ T shdyt[LX *LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dxt
#define NEKO_EB_MAX_SMEM
Definition elem_block.h:60
#define NEKO_EB_BOUNDS(NT)
Definition elem_block.h:95
@ DMMA_SI
@ DMMA_CUBE
@ DMMA_SJ
@ DMMA_MAT
@ DMMA_P
#define DMMA_NG
#define NEKO_DMMA_TMA_BATCH_SMEM
Defines a vector.
Definition vector.f90:34
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 T *__restrict__, const T *__restrict__, const T *__restrict__, const int *__restrict__, const int)
static __device__ void run(T *__restrict__, T *__restrict__, 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 T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const int *__restrict__)
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 T *__restrict__, const T *__restrict__, const T *__restrict__, const int *__restrict__)
static __device__ void run(T *__restrict__, T *__restrict__, 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 T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const int *__restrict__)
static __device__ void run(T *__restrict__, T *__restrict__, 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 T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const T *__restrict__, const int *__restrict__)