Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
ax_helm.hip
Go to the documentation of this file.
1/*
2 Copyright (c) 2021-2026, The Neko Authors
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 * Redistributions in binary form must reproduce the above
13 copyright notice, this list of conditions and the following
14 disclaimer in the documentation and/or other materials provided
15 with the distribution.
16
17 * Neither the name of the authors nor the names of its
18 contributors may be used to endorse or promote products derived
19 from this software without specific prior written permission.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 POSSIBILITY OF SUCH DAMAGE.
33*/
34
35#include <string.h>
36#include <stdlib.h>
37#include <stdio.h>
38#include <hip/hip_runtime.h>
40#include <device/hip/check.h>
41#include "ax_helm_kernel.h"
42#include "elem_block_tune.h"
43
44extern "C" {
45 #include <common/neko_log.h>
46}
47
48template < const int>
49int tune(void *w, void *u, void *dx, void *dy, void *dz,
50 void *dxt, void *dyt, void *dzt, void *h1,
51 void *g11, void *g22, void *g33, void *g12,
52 void *g13, void *g23, int *nelv, int *lx, int *eb_sel, int *ch_sel,
53 int *nwf_sel);
54
55template < const int>
56int tune_padded(void *w, void *u, void *dx, void *dy, void *dz,
57 void *dxt, void *dyt, void *dzt, void *h1,
58 void *g11, void *g22, void *g33, void *g12,
59 void *g13, void *g23, int *nelv, int *lx, int *eb_sel,
60 int *ch_sel, int *nwf_sel);
61
62template < const int>
63int tune_vector(void *au, void *av, void *aw, void *u, void *v, void *w,
64 void *dx, void *dy, void *dz, void *h1,
65 void *g11, void *g22, void *g33, void *g12,
66 void *g13, void *g23, int *nelv, int *lx);
67
68template < const int>
69int tune_vector_padded(void *au, void *av, void *aw,
70 void *u, void *v, void *w,
71 void *dx, void *dy, void *dz, void *h1,
72 void *g11, void *g22, void *g33, void *g12,
73 void *g13, void *g23, int *nelv, int *lx);
74
75extern "C" {
76
80 void hip_ax_helm(void *w, void *u, void *dx, void *dy, void *dz,
81 void *dxt, void *dyt, void *dzt, void *h1,
82 void *g11, void *g22, void *g33, void *g12,
83 void *g13, void *g23, int *nelv, int *lx) {
84
85 static int autotune[13] = { 0 };
86 /* Elements per block candidate chosen by the autotuner, see
87 elem_block<> in elem_block.h */
88 static int autotune_eb[13] = { 0 };
89 /* chunk candidate chosen for the 1d variant */
90 static int autotune_ch[13] = { 0 };
91 /* wavefronts per block candidate chosen for the mfma variant */
92 static int autotune_nwf[13] = { 0 };
93
94 const dim3 nthrds_1d(1024, 1, 1);
95 const dim3 nblcks_1d((*nelv), 1, 1);
96
97#define CASE_1D(LX, C) \
98 hipLaunchKernelGGL(HIP_KERNEL_NAME( \
99 ax_helm_kernel_1d<real, LX, NEKO_CHUNKS(LX, C)> ), \
100 nblcks_1d, NEKO_CHUNKS_NTHRDS(LX, C), 0, \
101 (hipStream_t) glb_cmd_queue, \
102 (real *) w, (real *) u, \
103 (real *) dx, (real *) dy, (real *) dz, \
104 (real *) dxt, (real *) dyt, (real *) dzt, (real *) h1, \
105 (real *) g11, (real *) g22, (real *) g33, \
106 (real *) g12, (real *) g13, (real *) g23); \
107 HIP_CHECK(hipGetLastError());
108
109/* Runtime dispatch onto the tuned chunk candidate */
110#define CASE_1D_SEL(LX, SEL) \
111 switch (SEL) { \
112 case 0: CASE_1D(LX, 0); break; \
113 case 1: CASE_1D(LX, 1); break; \
114 case 2: CASE_1D(LX, 2); break; \
115 default: CASE_1D(LX, 3); break; \
116 }
117
118#define CASE_KSTEP(LX, C) \
119 hipLaunchKernelGGL( HIP_KERNEL_NAME( \
120 ax_helm_kernel_kstep<real, LX, NEKO_EB(LX, C)> ), \
121 NEKO_EB_NBLCKS(*nelv, LX, C), NEKO_EB_NTHRDS(LX, C), 0, \
122 (hipStream_t) glb_cmd_queue, \
123 (real *) w, (real *) u, \
124 (real *) dx, (real *) dy, (real *) dz, (real *) h1, \
125 (real *) g11, (real *) g22, (real *) g33, \
126 (real *) g12, (real *) g13, (real *) g23, *nelv); \
127 HIP_CHECK(hipGetLastError());
128
129#define CASE_KSTEP_PADDED(LX, C) \
130 hipLaunchKernelGGL( HIP_KERNEL_NAME( \
131 ax_helm_kernel_kstep_padded<real, LX, NEKO_EB(LX, C)> ),\
132 NEKO_EB_NBLCKS(*nelv, LX, C), NEKO_EB_NTHRDS(LX, C), 0, \
133 (hipStream_t) glb_cmd_queue, \
134 (real *) w, (real *) u, \
135 (real *) dx, (real *) dy, (real *) dz, (real *) h1, \
136 (real *) g11, (real *) g22, (real *) g33, \
137 (real *) g12, (real *) g13, (real *) g23, *nelv); \
138 HIP_CHECK(hipGetLastError());
139
140#define CASE_MFMA(LX, C) \
141 hipLaunchKernelGGL( HIP_KERNEL_NAME( \
142 ax_helm_kernel_mfma<real, LX, NEKO_MFMA_NWF(C)> ), \
143 NEKO_MFMA_NBLCKS(*nelv, LX, C), NEKO_MFMA_NTHRDS(C), 0, \
144 (hipStream_t) glb_cmd_queue, \
145 (real *) w, (real *) u, \
146 (real *) dx, (real *) dy, (real *) dz, (real *) h1, \
147 (real *) g11, (real *) g22, (real *) g33, \
148 (real *) g12, (real *) g13, (real *) g23, *nelv); \
149 HIP_CHECK(hipGetLastError());
150
151/* Runtime dispatch onto the tuned wavefronts per block candidate */
152#define CASE_MFMA_SEL(LX, SEL) \
153 switch (SEL) { \
154 case 0: CASE_MFMA(LX, 0); break; \
155 case 1: CASE_MFMA(LX, 1); break; \
156 case 2: CASE_MFMA(LX, 2); break; \
157 default: CASE_MFMA(LX, 3); break; \
158 }
159
160/* Runtime dispatch onto the tuned elements per block candidate */
161#define CASE_KSTEP_SEL(LX, SEL) \
162 switch (SEL) { \
163 case 0: CASE_KSTEP(LX, 0); break; \
164 case 1: CASE_KSTEP(LX, 1); break; \
165 default: CASE_KSTEP(LX, 2); break; \
166 }
167
168#define CASE_KSTEP_PADDED_SEL(LX, SEL) \
169 switch (SEL) { \
170 case 0: CASE_KSTEP_PADDED(LX, 0); break; \
171 case 1: CASE_KSTEP_PADDED(LX, 1); break; \
172 default: CASE_KSTEP_PADDED(LX, 2); break; \
173 }
174
175#define CASE(LX) \
176 case LX: \
177 if(autotune[LX] == 0 ) { \
178 autotune[LX]=tune<LX>( w, u, \
179 dx, dy, dz, \
180 dxt, dyt, dzt,h1, \
181 g11, g22, g33, \
182 g12, g13, g23, nelv, lx, \
183 &autotune_eb[LX], &autotune_ch[LX], \
184 &autotune_nwf[LX]); \
185 } else if (autotune[LX] == 1 ) { \
186 CASE_1D_SEL(LX, autotune_ch[LX]); \
187 } else if (autotune[LX] == 2 ) { \
188 CASE_KSTEP_SEL(LX, autotune_eb[LX]); \
189 } else if (autotune[LX] == 3 ) { \
190 CASE_MFMA_SEL(LX, autotune_nwf[LX]); \
191 } \
192 break
193
194#define CASE_PADDED(LX) \
195 case LX: \
196 if(autotune[LX] == 0 ) { \
197 autotune[LX]=tune_padded<LX>(w, u, \
198 dx, dy, dz, \
199 dxt, dyt, dzt,h1, \
200 g11, g22, g33, \
201 g12, g13, g23,nelv,lx, \
202 &autotune_eb[LX], &autotune_ch[LX], \
203 &autotune_nwf[LX]); \
204 } else if (autotune[LX] == 1 ) { \
205 CASE_1D_SEL(LX, autotune_ch[LX]); \
206 } else if (autotune[LX] == 2 ) { \
207 CASE_KSTEP_PADDED_SEL(LX, autotune_eb[LX]); \
208 } else if (autotune[LX] == 3 ) { \
209 CASE_MFMA_SEL(LX, autotune_nwf[LX]); \
210 } \
211 break
212
213/*
214 * High order cases have no 1d variant to compare against (its LDS footprint
215 * grows as LX^3), so they are not tuned and keep one element per block,
216 * i.e. candidate 0
217 */
218#define CASE_LARGE(LX) \
219 case LX: \
220 CASE_KSTEP(LX, 0); \
221 break
222
223#define CASE_LARGE_PADDED(LX) \
224 case LX: \
225 CASE_KSTEP_PADDED(LX, 0); \
226 break
227
228 if ((*lx) < 13) {
229 switch(*lx) {
230 CASE(2);
231 CASE(3);
232 CASE(4);
233 CASE(5);
234 CASE(6);
235 CASE(7);
236 CASE_PADDED(8);
237 CASE(9);
238 CASE(10);
239 CASE(11);
240 CASE(12);
241 default:
242 {
243 fprintf(stderr, __FILE__ ": size not supported: %d\n", *lx);
244 exit(1);
245 }
246 }
247 }
248 else {
249 switch(*lx) {
250 CASE_LARGE(13);
251 CASE_LARGE(14);
252 CASE_LARGE(15);
254 default:
255 {
256 fprintf(stderr, __FILE__ ": size not supported: %d\n", *lx);
257 exit(1);
258 }
259 }
260 }
261 }
265 void hip_ax_helm_vector(void *au, void *av, void *aw,
266 void *u, void *v, void *w,
267 void *dx, void *dy, void *dz,
268 void *dxt, void *dyt, void *dzt,
269 void *h1, void *g11, void *g22,
270 void *g33, void *g12, void *g13,
271 void *g23, int *nelv, int *lx) {
272
273 /* 0 = untuned, otherwise the chosen candidate plus one. Unlike the
274 scalar Ax there is no 1d variant to fall back on, so the tuner only
275 sweeps the elements per block candidates */
276 static int autotune_v[17] = { 0 };
277
278#define CASE_VECTOR_KSTEP(LX, C) \
279 hipLaunchKernelGGL( HIP_KERNEL_NAME( \
280 ax_helm_kernel_vector_kstep<real, LX, \
281 NEKO_EB(LX, C)> ), \
282 NEKO_EB_NBLCKS(*nelv, LX, C), NEKO_EB_NTHRDS(LX, C), 0, \
283 (hipStream_t) glb_cmd_queue, \
284 (real *) au, (real *) av, (real *) aw, \
285 (real *) u, (real *) v, (real *) w, \
286 (real *) dx, (real *) dy, (real *) dz, (real *) h1, \
287 (real *) g11, (real *) g22, (real *) g33, \
288 (real *) g12, (real *) g13, (real *) g23, *nelv); \
289 HIP_CHECK(hipGetLastError());
290
291#define CASE_VECTOR_KSTEP_PADDED(LX, C) \
292 hipLaunchKernelGGL( HIP_KERNEL_NAME( \
293 ax_helm_kernel_vector_kstep_padded<real, LX, \
294 NEKO_EB(LX, C)> ), \
295 NEKO_EB_NBLCKS(*nelv, LX, C), NEKO_EB_NTHRDS(LX, C), 0, \
296 (hipStream_t) glb_cmd_queue, \
297 (real *) au, (real *) av, (real *) aw, \
298 (real *) u, (real *) v, (real *) w, \
299 (real *) dx, (real *) dy, (real *) dz, (real *) h1, \
300 (real *) g11, (real *) g22, (real *) g33, \
301 (real *) g12, (real *) g13, (real *) g23, *nelv); \
302 HIP_CHECK(hipGetLastError());
303
304#define CASE_VECTOR_SEL(LX, SEL) \
305 switch (SEL) { \
306 case 0: CASE_VECTOR_KSTEP(LX, 0); break; \
307 case 1: CASE_VECTOR_KSTEP(LX, 1); break; \
308 default: CASE_VECTOR_KSTEP(LX, 2); break; \
309 }
310
311#define CASE_VECTOR_PADDED_SEL(LX, SEL) \
312 switch (SEL) { \
313 case 0: CASE_VECTOR_KSTEP_PADDED(LX, 0); break; \
314 case 1: CASE_VECTOR_KSTEP_PADDED(LX, 1); break; \
315 default: CASE_VECTOR_KSTEP_PADDED(LX, 2); break; \
316 }
317
318#define CASE_VECTOR(LX) \
319 case LX: \
320 if (autotune_v[LX] == 0) { \
321 autotune_v[LX] = 1 + tune_vector<LX>(au, av, aw, u, v, w, \
322 dx, dy, dz, h1, \
323 g11, g22, g33, \
324 g12, g13, g23, nelv, lx); \
325 } else { \
326 CASE_VECTOR_SEL(LX, autotune_v[LX] - 1); \
327 } \
328 break
329
330#define CASE_VECTOR_PADDED(LX) \
331 case LX: \
332 if (autotune_v[LX] == 0) { \
333 autotune_v[LX] = 1 + tune_vector_padded<LX>(au, av, aw, u, v, w, \
334 dx, dy, dz, h1, \
335 g11, g22, g33, \
336 g12, g13, g23, nelv, lx); \
337 } else { \
338 CASE_VECTOR_PADDED_SEL(LX, autotune_v[LX] - 1); \
339 } \
340 break
341
342 switch(*lx) {
343 CASE_VECTOR(2);
344 CASE_VECTOR(3);
346 CASE_VECTOR(5);
347 CASE_VECTOR(6);
348 CASE_VECTOR(7);
350 CASE_VECTOR(9);
351 CASE_VECTOR(10);
352 CASE_VECTOR(11);
353 CASE_VECTOR(12);
354 CASE_VECTOR(13);
355 CASE_VECTOR(14);
356 CASE_VECTOR(15);
358 default:
359 {
360 fprintf(stderr, __FILE__ ": size not supported: %d\n", *lx);
361 exit(1);
362 }
363 }
364 }
365
369 void hip_ax_helm_vector_part2(void *au, void *av, void *aw,
370 void *u, void *v, void *w,
371 void *h2, void *B, int *n) {
372
373 const dim3 nthrds(1024, 1, 1);
374 const dim3 nblcks(((*n)+1024 - 1)/ 1024, 1, 1);
375 const hipStream_t stream = (hipStream_t) glb_cmd_queue;
376
378 nblcks, nthrds, 0, stream,
379 (real *) au, (real *) av, (real *) aw,
380 (real *) u, (real *) v, (real *) w,
381 (real *) h2, (real *) B, *n);
382 }
383
384}
385
386template < const int LX >
387int tune(void *w, void *u, void *dx, void *dy, void *dz,
388 void *dxt, void *dyt, void *dzt, void *h1,
389 void *g11, void *g22, void *g33, void *g12,
390 void *g13, void *g23, int *nelv, int *lx, int *eb_sel, int *ch_sel,
391 int *nwf_sel) {
394 int best1 = 0;
397 int best3 = 0;
398 const int rounds = neko_tune_rounds();
399 const int iters = neko_tune_iters();
400 const int sweep = neko_eb_sweep();
401 /* Hardware capability, which the explicit NEKO_AUTOTUNE=MFMA pin needs */
402 const bool mfma = mfma_lx_supported<LX>() && hip_have_mfma();
403 /* Whether the sweep may pick it on its own, see neko_mfma_sweep() */
404 const bool mfma_tune = mfma && neko_mfma_sweep();
405 int best = 0;
406 int retval;
407
408 for (int c = 0; c < NEKO_EB_CANDIDATES; c++) {
410 }
411 for (int c = 0; c < NEKO_CHUNKS_CANDIDATES; c++) {
413 }
414 for (int c = 0; c < NEKO_MFMA_CANDIDATES; c++) {
416 }
417
418 const dim3 nthrds_1d(1024, 1, 1);
419 const dim3 nblcks_1d((*nelv), 1, 1);
420 const hipStream_t stream = (hipStream_t) glb_cmd_queue;
421
422 char *env_value = NULL;
423 char neko_log_buf[80];
424
425 env_value=getenv("NEKO_AUTOTUNE");
426
427 sprintf(neko_log_buf, "Autotune Ax helm (lx: %d)", *lx);
429
430 *eb_sel = 0;
431 *ch_sel = 0;
432
433 if(env_value) {
434 if( !strcmp(env_value,"1D") ) {
437 sprintf(neko_log_buf,"Set by env : 1 (1D, %d chunk)",
441 return 1;
442 } else if( !strcmp(env_value,"KSTEP") ) {
443 const int c = neko_eb_env();
444 *eb_sel = c;
445 CASE_KSTEP_SEL(LX, c);
446 sprintf(neko_log_buf,"Set by env : 2 (KSTEP, %d elem/block)",
447 NEKO_EB_SEL(LX, c));
450 return 2;
451 } else if( !strcmp(env_value,"MFMA") ) {
452 if (mfma) {
453 const int c = neko_mfma_env();
454 *nwf_sel = c;
455 CASE_MFMA_SEL(LX, c);
456 sprintf(neko_log_buf,"Set by env : 3 (MFMA, %d wf, %d elem/block)",
460 return 3;
461 } else {
462 sprintf(neko_log_buf, "MFMA strategy not available for this config");
464 }
465 } else {
466 sprintf(neko_log_buf, "Invalid value set for NEKO_AUTOTUNE");
468 }
469 }
470
473
474 /* Warm every variant before timing anything: each specialisation has to
475 be resident and the clocks at steady state, or whichever variant is
476 timed first is measured on a colder part */
477 for (int i = 0; i < NEKO_TUNE_WARMUP; i++) {
478 CASE_1D(LX, 0);
479 CASE_1D(LX, 1);
480 CASE_1D(LX, 2);
481 CASE_1D(LX, 3);
482 CASE_KSTEP(LX, 0);
483 if (sweep) {
484 CASE_KSTEP(LX, 1);
485 CASE_KSTEP(LX, 2);
486 }
487 if (mfma_tune) {
488 CASE_MFMA(LX, 0);
489 CASE_MFMA(LX, 1);
490 CASE_MFMA(LX, 2);
491 CASE_MFMA(LX, 3);
492 }
493 }
494
495 /* Interleaved rounds, best time per variant: timing them one after another
496 in a fixed order lets clock drift bias the comparison by position */
497 for (int r = 0; r < rounds; r++) {
503 if (sweep) {
506 }
507 if (mfma_tune) {
512 }
513 }
514
517
521 *eb_sel = best;
522 *ch_sel = best1;
523 *nwf_sel = best3;
524
525 if (time1[best1] < time2[best]) {
526 retval = 1;
527 } else {
528 retval = 2;
529 }
530
531 /* The mfma variant joins the comparison only where it exists, its
532 candidates are left at NEKO_TUNE_INIT otherwise */
533 if (time3[best3] < ((retval == 1) ? time1[best1] : time2[best])) {
534 retval = 3;
535 }
536
539
540 /* The tuner stands in for a real Ax evaluation, and the variants do not
541 sum in the same order, so leave the output of the chosen kernel in w */
542 if (retval == 1) {
544 } else if (retval == 2) {
546 } else {
548 }
549
550 if (retval == 1) {
551 sprintf(neko_log_buf, "Chose : 1 (1D, %d chunk)",
553 } else if (retval == 2) {
554 sprintf(neko_log_buf, "Chose : 2 (KSTEP, %d elem/block)",
556 } else {
557 sprintf(neko_log_buf, "Chose : 3 (MFMA, %d wf, %d elem/block)",
559 }
562 return retval;
563}
564
565template < const int LX >
566int tune_padded(void *w, void *u, void *dx, void *dy, void *dz,
567 void *dxt, void *dyt, void *dzt, void *h1,
568 void *g11, void *g22, void *g33, void *g12,
569 void *g13, void *g23, int *nelv, int *lx,
570 int *eb_sel, int *ch_sel, int *nwf_sel) {
573 int best1 = 0;
576 int best3 = 0;
577 const int rounds = neko_tune_rounds();
578 const int iters = neko_tune_iters();
579 const int sweep = neko_eb_sweep();
580 /* Hardware capability, which the explicit NEKO_AUTOTUNE=MFMA pin needs */
581 const bool mfma = mfma_lx_supported<LX>() && hip_have_mfma();
582 /* Whether the sweep may pick it on its own, see neko_mfma_sweep() */
583 const bool mfma_tune = mfma && neko_mfma_sweep();
584 int best = 0;
585 int retval;
586
587 for (int c = 0; c < NEKO_EB_CANDIDATES; c++) {
589 }
590 for (int c = 0; c < NEKO_CHUNKS_CANDIDATES; c++) {
592 }
593 for (int c = 0; c < NEKO_MFMA_CANDIDATES; c++) {
595 }
596
597 const dim3 nthrds_1d(1024, 1, 1);
598 const dim3 nblcks_1d((*nelv), 1, 1);
599 const hipStream_t stream = (hipStream_t) glb_cmd_queue;
600
601 char *env_value = NULL;
602 char neko_log_buf[80];
603
604 env_value=getenv("NEKO_AUTOTUNE");
605
606 sprintf(neko_log_buf, "Autotune Ax helm (lx: %d)", *lx);
608
609 *eb_sel = 0;
610 *ch_sel = 0;
611
612 if(env_value) {
613 if( !strcmp(env_value,"1D") ) {
616 sprintf(neko_log_buf,"Set by env : 1 (1D, %d chunk)",
620 return 1;
621 } else if( !strcmp(env_value,"KSTEP") ) {
622 const int c = neko_eb_env();
623 *eb_sel = c;
625 sprintf(neko_log_buf,"Set by env : 2 (KSTEP, %d elem/block)",
626 NEKO_EB_SEL(LX, c));
629 return 2;
630 } else if( !strcmp(env_value,"MFMA") ) {
631 if (mfma) {
632 const int c = neko_mfma_env();
633 *nwf_sel = c;
634 CASE_MFMA_SEL(LX, c);
635 sprintf(neko_log_buf,"Set by env : 3 (MFMA, %d wf, %d elem/block)",
639 return 3;
640 } else {
641 sprintf(neko_log_buf, "MFMA strategy not available for this config");
643 }
644 } else {
645 sprintf(neko_log_buf, "Invalid value set for NEKO_AUTOTUNE");
647 }
648 }
649
652
653 /* Warm every variant before timing anything: each specialisation has to
654 be resident and the clocks at steady state, or whichever variant is
655 timed first is measured on a colder part */
656 for (int i = 0; i < NEKO_TUNE_WARMUP; i++) {
657 CASE_1D(LX, 0);
658 CASE_1D(LX, 1);
659 CASE_1D(LX, 2);
660 CASE_1D(LX, 3);
662 if (sweep) {
665 }
666 if (mfma_tune) {
667 CASE_MFMA(LX, 0);
668 CASE_MFMA(LX, 1);
669 CASE_MFMA(LX, 2);
670 CASE_MFMA(LX, 3);
671 }
672 }
673
674 /* Interleaved rounds, best time per variant: timing them one after another
675 in a fixed order lets clock drift bias the comparison by position */
676 for (int r = 0; r < rounds; r++) {
682 if (sweep) {
685 }
686 if (mfma_tune) {
691 }
692 }
693
696
700 *eb_sel = best;
701 *ch_sel = best1;
702 *nwf_sel = best3;
703
704 if (time1[best1] < time2[best]) {
705 retval = 1;
706 } else {
707 retval = 2;
708 }
709
710 /* The mfma variant joins the comparison only where it exists, its
711 candidates are left at NEKO_TUNE_INIT otherwise */
712 if (time3[best3] < ((retval == 1) ? time1[best1] : time2[best])) {
713 retval = 3;
714 }
715
718
719 /* Leave the output of the chosen kernel in w, see tune() */
720 if (retval == 1) {
722 } else if (retval == 2) {
724 } else {
726 }
727
728 if (retval == 1) {
729 sprintf(neko_log_buf, "Chose : 1 (1D, %d chunk)",
731 } else if (retval == 2) {
732 sprintf(neko_log_buf, "Chose : 2 (KSTEP, %d elem/block)",
734 } else {
735 sprintf(neko_log_buf, "Chose : 3 (MFMA, %d wf, %d elem/block)",
737 }
740 return retval;
741}
742
743/*
744 * The vector Ax has no 1d variant, so these only sweep the elements per
745 * block candidates. Setting NEKO_AUTOTUNE to anything pins candidate 0,
746 * i.e. the one element per block shape these kernels have always had,
747 * which is the A/B baseline for the blocking.
748 *
749 * @note The section title has to fit the 30 character header field in
750 * log_section(), which truncates rather than wraps -- hence "Ax vector"
751 * rather than the "Ax helm vector" it reads as. It matches the CUDA copy.
752 */
753template < const int LX >
754int tune_vector(void *au, void *av, void *aw, void *u, void *v, void *w,
755 void *dx, void *dy, void *dz, void *h1,
756 void *g11, void *g22, void *g33, void *g12,
757 void *g13, void *g23, int *nelv, int *lx) {
759 float time[NEKO_EB_CANDIDATES];
760 const int rounds = neko_tune_rounds();
761 const int iters = neko_tune_iters();
762 const int sweep = neko_eb_sweep();
763 int best = 0;
764
765 for (int c = 0; c < NEKO_EB_CANDIDATES; c++) {
766 time[c] = NEKO_TUNE_INIT;
767 }
768
769 const hipStream_t stream = (hipStream_t) glb_cmd_queue;
770 char neko_log_buf[80];
771
772 sprintf(neko_log_buf, "Autotune Ax vector (lx: %d)", *lx);
774
775 /*
776 * Default to one element per block, as on the CUDA backend. These kernels
777 * carry six T[LX] register arrays against the scalar kernels' two, so they
778 * reach the register file limit -- and start spilling to scratch -- well
779 * before a wider block can buy anything. Sweeping them by default was a
780 * regression. Set NEKO_EB_TUNE=1 to opt back into the sweep
781 */
782 if (getenv("NEKO_AUTOTUNE") ||
783 getenv("NEKO_EB") || !neko_eb_sweep()) {
784 const int c = neko_eb_env();
786 sprintf(neko_log_buf, "Vector Ax : %d elem/block (untuned)",
787 NEKO_EB_SEL(LX, c));
790 return c;
791 }
792
795
796 for (int i = 0; i < NEKO_TUNE_WARMUP; i++) {
800 }
801
802 for (int r = 0; r < rounds; r++) {
806 }
807
808 for (int c = 0; c < NEKO_EB_CANDIDATES; c++) {
809 sprintf(neko_log_buf, "eb=%-4d : %9.2f us/call",
810 NEKO_EB_SEL(LX, c), NEKO_TUNE_US(time[c], iters));
812 }
813
815
818
819 /* Leave the output of the chosen kernel in au/av/aw */
821
822 sprintf(neko_log_buf, "Chose : %d elem/block", NEKO_EB_SEL(LX, best));
825 return best;
826}
827
828template < const int LX >
829int tune_vector_padded(void *au, void *av, void *aw,
830 void *u, void *v, void *w,
831 void *dx, void *dy, void *dz, void *h1,
832 void *g11, void *g22, void *g33, void *g12,
833 void *g13, void *g23, int *nelv, int *lx) {
835 float time[NEKO_EB_CANDIDATES];
836 const int rounds = neko_tune_rounds();
837 const int iters = neko_tune_iters();
838 const int sweep = neko_eb_sweep();
839 int best = 0;
840
841 for (int c = 0; c < NEKO_EB_CANDIDATES; c++) {
842 time[c] = NEKO_TUNE_INIT;
843 }
844
845 const hipStream_t stream = (hipStream_t) glb_cmd_queue;
846 char neko_log_buf[80];
847
848 sprintf(neko_log_buf, "Autotune Ax vector (lx: %d)", *lx);
850
851 /*
852 * Default to one element per block, as on the CUDA backend. These kernels
853 * carry six T[LX] register arrays against the scalar kernels' two, so they
854 * reach the register file limit -- and start spilling to scratch -- well
855 * before a wider block can buy anything. Sweeping them by default was a
856 * regression. Set NEKO_EB_TUNE=1 to opt back into the sweep
857 */
858 if (getenv("NEKO_AUTOTUNE") ||
859 getenv("NEKO_EB") || !neko_eb_sweep()) {
860 const int c = neko_eb_env();
862 sprintf(neko_log_buf, "Vector Ax : %d elem/block (untuned)",
863 NEKO_EB_SEL(LX, c));
866 return c;
867 }
868
871
872 for (int i = 0; i < NEKO_TUNE_WARMUP; i++) {
876 }
877
878 for (int r = 0; r < rounds; r++) {
882 }
883
884 for (int c = 0; c < NEKO_EB_CANDIDATES; c++) {
885 sprintf(neko_log_buf, "eb=%-4d : %9.2f us/call",
886 NEKO_EB_SEL(LX, c), NEKO_TUNE_US(time[c], iters));
888 }
889
891
894
895 /* Leave the output of the chosen kernel in au/av/aw */
897
898 sprintf(neko_log_buf, "Chose : %d elem/block", NEKO_EB_SEL(LX, best));
901 return best;
902}
int tune_padded(void *w, void *u, void *dx, void *dy, void *dz, void *dxt, void *dyt, void *dzt, void *h1, void *g11, void *g22, void *g33, void *g12, void *g13, void *g23, int *nelv, int *lx, int *eb_sel, int *ch_sel, int *nwf_sel)
Definition ax_helm.hip:566
#define CASE_VECTOR(LX)
#define CASE_KSTEP_SEL(LX, SEL)
#define CASE(LX)
void hip_ax_helm_vector_part2(void *au, void *av, void *aw, void *u, void *v, void *w, void *h2, void *B, int *n)
Definition ax_helm.hip:369
#define CASE_1D_SEL(LX, SEL)
#define CASE_VECTOR_KSTEP(LX, C)
#define CASE_VECTOR_PADDED_SEL(LX, SEL)
#define CASE_MFMA_SEL(LX, SEL)
#define CASE_PADDED(LX)
#define CASE_KSTEP(LX, C)
#define CASE_KSTEP_PADDED_SEL(LX, SEL)
#define CASE_LARGE(LX)
int tune_vector(void *au, void *av, void *aw, void *u, void *v, void *w, void *dx, void *dy, void *dz, void *h1, void *g11, void *g22, void *g33, void *g12, void *g13, void *g23, int *nelv, int *lx)
Definition ax_helm.hip:754
void hip_ax_helm_vector(void *au, void *av, void *aw, void *u, void *v, void *w, void *dx, void *dy, void *dz, void *dxt, void *dyt, void *dzt, void *h1, void *g11, void *g22, void *g33, void *g12, void *g13, void *g23, int *nelv, int *lx)
Definition ax_helm.hip:265
int tune_vector_padded(void *au, void *av, void *aw, void *u, void *v, void *w, void *dx, void *dy, void *dz, void *h1, void *g11, void *g22, void *g33, void *g12, void *g13, void *g23, int *nelv, int *lx)
Definition ax_helm.hip:829
void hip_ax_helm(void *w, void *u, void *dx, void *dy, void *dz, void *dxt, void *dyt, void *dzt, void *h1, void *g11, void *g22, void *g33, void *g12, void *g13, void *g23, int *nelv, int *lx)
Definition ax_helm.hip:80
#define CASE_LARGE_PADDED(LX)
#define CASE_VECTOR_KSTEP_PADDED(LX, C)
#define CASE_MFMA(LX, C)
#define CASE_1D(LX, C)
#define CASE_VECTOR_PADDED(LX)
#define CASE_VECTOR_SEL(LX, SEL)
#define CASE_KSTEP_PADDED(LX, C)
int tune(void *w, void *u, void *dx, void *dy, void *dz, void *dxt, void *dyt, void *dzt, void *h1, void *g11, void *g22, void *g33, void *g12, void *g13, void *g23, int *nelv, int *lx, int *eb_sel, int *ch_sel, int *nwf_sel)
Definition ax_helm.hip:387
__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)
__global__ void T *__restrict__ T *__restrict__ aw
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ w
const int i
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dz
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dx
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ u
__global__ void T *__restrict__ av
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dy
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ v
__global__ void 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__ h1
__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
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g22
__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 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 nelv
__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__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g33
__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__ const T *__restrict__ dyt
__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__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dxt
#define NEKO_CHUNKS_CANDIDATES
Definition elem_block.h:125
#define NEKO_EB_CANDIDATES
Definition elem_block.h:63
#define NEKO_EB_SEL(LX, SEL)
Definition elem_block.h:108
#define NEKO_CHUNKS_SEL(LX, SEL)
Definition elem_block.h:147
#define NEKO_TUNE_TIME(T, LAUNCH, LX, C, ITERS)
static int neko_eb_env()
#define NEKO_TUNE_US(T, ITERS)
static int neko_tune_rounds()
#define NEKO_TUNE_LOG(LX, T1, T2)
#define NEKO_TUNE_INIT
#define NEKO_TUNE_BEST(T, BEST, N)
static int neko_tune_iters()
static int neko_chunks_env()
static int neko_eb_sweep()
#define NEKO_TUNE_WARMUP
double real
#define HIP_CHECK(err)
Definition check.h:8
#define NEKO_TUNE_LOG_MFMA(LX, T3)
#define NEKO_MFMA_CANDIDATES
static bool hip_have_mfma()
static int neko_mfma_env()
#define NEKO_MFMA_NWF(C)
#define NEKO_MFMA_EB(LX, C)
static int neko_mfma_sweep()
void log_error(char *msg)
void log_message(char *msg)
void log_end_section()
void log_section(char *msg)