Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
mfma_kernel.h
Go to the documentation of this file.
1#ifndef __MATH_MFMA_KERNEL_H__
2#define __MATH_MFMA_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
100#include <stdlib.h>
101#include <string.h>
102#include <hip/hip_runtime.h>
103#include <device/device_config.h>
104#include <device/hip/check.h>
105/* NEKO_EB_MAX_LDS, which the elements-per-block ladder below is clamped by */
106#include "elem_block.h"
107
108/*
109 * Reports whether the device code really was compiled for a matrix core
110 * architecture, i.e. whether the __gfx90a__ / __gfx942__ guard below was true
111 * in the device pass.
112 *
113 * This is not the same question as "does the device have matrix cores". The
114 * contraction primitives and their call sites are all guarded on those
115 * macros, so a build whose offload arch does not include the running device's
116 * -- or a code object selected from a fat binary built for something else --
117 * turns the whole strategy into a silent no-op: the kernel launches, writes
118 * nothing, and leaves stale values in the output. That fails as bad results
119 * rather than as an error, which is the worst way for it to fail. Checking it
120 * from the device removes the guesswork.
121 */
122/* static, not merely file scope by convention: this header is included by
123 every operator that offers an MFMA strategy -- ax_helm, dudxyz, opgrad,
124 conv1 and cdtp -- and a non-template __global__ with external linkage is
125 then defined once per translation unit, which the linker rejects as a
126 multiple definition. Internal linkage gives each unit its own copy, which
127 is what the rest of this header already relies on. */
128static __global__ void hip_mfma_arch_probe(int * flag) {
129#if defined(__gfx90a__) || defined(__gfx942__)
130 *flag = 1;
131#else
132 *flag = 0;
133#endif
134}
135
144static inline bool hip_have_mfma() {
145 static int cached = -1;
146 if (cached < 0) {
147 int dev = 0;
149 cached = 0;
150 if (hipGetDevice(&dev) == hipSuccess &&
152 (strstr(prop.gcnArchName, "gfx90a") != NULL ||
153 strstr(prop.gcnArchName, "gfx942") != NULL)) {
154 int *d_flag = NULL;
155 int flag = 0;
156 if (hipMalloc(&d_flag, sizeof(int)) == hipSuccess) {
157 if (hipMemcpy(d_flag, &flag, sizeof(int),
160 d_flag);
161 if (hipGetLastError() == hipSuccess &&
162 hipMemcpy(&flag, d_flag, sizeof(int),
164 cached = flag;
165 }
166 }
167 /* Unlike the queries above, a failure here is not "the strategy is
168 unavailable" -- the pointer came from a hipMalloc that succeeded, so
169 a bad free means the context is broken. Checked rather than folded
170 into cached, and checked rather than discarded: hipFree is
171 nodiscard */
173 }
174 }
175 }
176 return cached == 1;
177}
178
198template < const int LX >
199static inline bool mfma_lx_supported() {
200 if (sizeof(real) == 4) {
201 return (LX >= 2) && (LX <= 12);
202 }
203 return (sizeof(real) == 8) && (LX >= 4) && (LX <= 12);
204}
205
206/*
207 * Wavefronts per block for the MFMA kernels. Candidate C selects 2^C
208 * wavefronts, i.e. 1, 2, 4 or 8.
209 *
210 * Two things are being traded. The contraction stripes its N column groups
211 * across wavefronts, and there are only NGROUPS = ceil(LX^2/16) of them --
212 * one at LX = 4, four at LX = 8 -- so past that count the extra wavefronts
213 * idle through the matrix core work. The staging and pointwise loops, on the
214 * other hand, keep scaling: at LX = 8, eight wavefronts is 512 threads for
215 * 512 points, one each, which is why the branch this came from defaulted to
216 * eight. The measured LX = 8 curve was still improving at four, hence the
217 * fourth candidate.
218 */
219/*
220 * The candidate space is two dimensional: the wavefronts per block above, and
221 * which matrix core tile the contraction is issued on. Candidate C encodes
222 * both -- NWF = 2^(C mod 4) and TILE = C / 4 -- so one selector still names a
223 * whole geometry and candidates 0..3 mean exactly what they always did.
224 *
225 * TILE 0 is the precision's batched tile: the 4x4x4 in double precision,
226 * the 4x4x1 in single (which also batches elements, see NEKO_MFMA_NEL_N).
227 * TILE 1 is the 16x16x4 tile in both. Under -DMFMA_F64_USE_16X16 /
228 * -DMFMA_F32_USE_16X16 a precision's two tiles are the same code and only
229 * TILE 0 is offered there -- see mfma_tile_offered().
230 *
231 * WHY THE TILE IS MEASURED RATHER THAN CHOSEN. It used to be a compile-time
232 * #ifdef, on the argument that the 4x4x4 tile fills M = LX < 16 exactly where
233 * the 16x16x4 one wastes half its rows at LX = 8. That argument tacitly
234 * assumes the two instructions run at the same rate, and they do not. From
235 * AMD's own matrix instruction calculator, identically on gfx90a and gfx942:
236 *
237 * v_mfma_f64_16x16x4f64 2048 flop / 32 cyc = 256 flop/CU/cycle, 0 wait
238 * v_mfma_f64_4x4x4f64 512 flop / 16 cyc = 128 flop/CU/cycle, 4 waits
239 *
240 * The small tile is rate-halved -- 128 flop/CU/cycle is the f64 *vector* rate,
241 * so it surrenders the whole reason to use a matrix core in double precision
242 * -- and a chain of them into one accumulator, which is exactly what
243 * mfma_contract_4x4's K loop is, owes four cycles per step where the large
244 * tile owes none. Counting issues per element per contraction (4x4x4:
245 * ceil(LX/4) M-tiles x groups x ceil(LX/4) K-steps at 16 cycles; 16x16x4:
246 * groups x K-steps at 32) the M-utilisation gain is exactly cancelled at
247 * LX = 5..8 and reversed beyond it: 4x4x4 wins 2x at LX = 4, loses ~11% at
248 * LX = 5..8 once the accumulate waits are counted, and loses 1.5x at
249 * LX = 9..12. It also re-reads the B operand once per M-tile, so it costs 2x
250 * the operand traffic at LX = 5..8 and 3x at LX = 9..12 on a kernel measured
251 * at 69-72% of the memory roof.
252 *
253 * So the analytical answer is "4x4x4 at LX = 4, 16x16x4 above it", which is
254 * not what the old default did -- and it is an analytical answer, of the same
255 * kind as the one it replaces. Both tiles are hardware verified against a CPU
256 * reference and produce bit-identical f64 results, so trying both costs
257 * nothing but tuning time. Hence: measure it.
258 */
259/*
260 * THE 16-WAVEFRONT RUNG. The ladder used to stop at eight wavefronts, 512
261 * threads, and that ceiling is what the strategy was losing on at LX = 8
262 * rather than anything to do with matrix cores. The 1D kernel that beats it
263 * there stages MORE LDS -- 6*LX^2 + 4*LX^3 against this kernel's
264 * 3*LX^2 + 4*LX^3 -- but runs CHUNKS threads on one element, so its 1024- and
265 * 512-thread candidates reach 8.00 and 6.00 wavefronts per SIMD where this
266 * ladder tops out at 3.00. A block shape the tuner cannot express is a block
267 * shape it cannot choose, so the rung is added rather than argued about.
268 *
269 * 1024 threads is the CDNA workgroup maximum, so this is the last rung there
270 * is. It is only reachable where the LDS budget allows it, which is what the
271 * clamp in NEKO_MFMA_EB_N below is for: at LX = 8 in double precision the
272 * unclamped EB = NWF/NGROUPS = 4 would ask for 4 elements x 4 cubes and blow
273 * the 64 kB workgroup limit -- as a compile error in the kernel's
274 * static_assert, not as a bad launch, but a compile error all the same.
275 */
276#define NEKO_MFMA_NWF_CANDIDATES 5
277#define NEKO_MFMA_TILE_CANDIDATES 2
278#define NEKO_MFMA_CANDIDATES \
279 (NEKO_MFMA_NWF_CANDIDATES * NEKO_MFMA_TILE_CANDIDATES)
280#define NEKO_MFMA_NWF(C) (1 << ((C) % NEKO_MFMA_NWF_CANDIDATES))
281#define NEKO_MFMA_TILE(C) ((C) / NEKO_MFMA_NWF_CANDIDATES)
282#define NEKO_MFMA_NTHRDS(C) dim3(64, NEKO_MFMA_NWF(C), 1)
283
284/*
285 * LDS PADDING OF THE STAGED CUBE.
286 *
287 * The cube is stored i + SJ*j + SK*k with SJ = LX + PAD and SK = SJ*LX. PAD
288 * is not a free parameter: CDNA's LDS is 32 banks of one dword, a b32 access
289 * is serviced 32 lanes at a time and a b64 access 16 lanes at a time (16
290 * lanes x 2 dwords = 32 dwords), and distinct addresses landing on one bank
291 * inside such a group serialise. A contraction along AXIS = 0 walks its
292 * free index n = (j,k) across the lanes, so consecutive lanes are SJ elements
293 * apart, and the whole question is what SJ does modulo the bank count:
294 *
295 * SJ odd -> f64 lanes cover 16 distinct bank pairs, f32 lanes 32 distinct
296 * banks. Conflict free.
297 * SJ even -> the stride and the bank count share a factor and the group
298 * collapses onto a few banks. At LX = 8 in double precision that
299 * is an 8-WAY conflict on both the B read and the D store.
300 *
301 * So padding an even LX by one is what removes the conflict, and padding an
302 * odd LX would introduce one -- the opposite of the usual "always pad by one"
303 * reflex. AXIS = 2 is conflict free either way (its index is n + SK*p, unit
304 * stride across lanes), AXIS = 1 sits in between.
305 *
306 * MODELLED, NOT MEASURED: a bank simulator over every lane of every LDS
307 * access to the CUBE -- the six contractions' operand reads and result
308 * stores, and the three linear passes -- for every LX in 4..12, both
309 * precisions, both tiles. The derivative matrix is padded by the same policy
310 * and is counted separately, see NEKO_MFMA_SD_N. Cycles per element, best
311 * wavefront shape, B-hoist on, padded-slot linear passes:
312 *
313 * LX = 8 f64 4x4x4 : 1696 -> 836 (-51%)
314 * LX = 8 f32 16x16 : 816 -> 642 (-21%)
315 * LX = 12 f64 4x4x4 : 3996 -> 2808 (-30%)
316 * LX = 4 f64 4x4x4 : 180 -> 136 (-24%)
317 * LX = 4 f32 16x16 : 102 -> 108 (+6%, the one regression)
318 * LX = 6, 10 : within 1% either way
319 *
320 * Hence the policy below: pad where LX is a multiple of four, which is where
321 * the conflict is worst and the win is large, and leave LX = 4 in single
322 * precision alone. That last exception is not cosmetic -- LX = 4 f32 is the
323 * only configuration where this strategy has ever been measured to win
324 * anything (1.9% over 1D on gfx90a), so it does not get handed a 6%
325 * regression on a model. LX odd is never padded; LX = 6 and 10 gain nothing
326 * because 6 and 10 are already only 2-way, and padding them costs a bigger
327 * cube.
328 *
329 * -DNEKO_MFMA_PAD=0 forces the old unpadded layout and -DNEKO_MFMA_PAD=1 pads
330 * every even LX, for an A/B against the model. Odd LX is never padded whatever
331 * the setting, because there it is known to make things worse.
332 *
333 * Padding is a property of the layout, not of the lane mapping: which lane
334 * handles which (m, n) is untouched, so it is exactly the kind of change a
335 * host reference catches -- unlike a lane-layout change, where an internally
336 * consistent error cancels in write-back (see mfma_contract_4x4).
337 */
338#ifdef NEKO_MFMA_PAD
339#define NEKO_MFMA_PAD_N(LX, SZ) (((LX) % 2 == 0) ? (NEKO_MFMA_PAD) : 0)
340#else
341#define NEKO_MFMA_PAD_N(LX, SZ) \
342 ((((LX) % 4 == 0) && !((LX) == 4 && (SZ) == 4)) ? 1 : 0)
343#endif
344/* j-stride, k-stride and total slots of one staged cube */
345#define NEKO_MFMA_SJ_N(LX, SZ) ((LX) + NEKO_MFMA_PAD_N(LX, SZ))
346#define NEKO_MFMA_SK_N(LX, SZ) (NEKO_MFMA_SJ_N(LX, SZ) * (LX))
347#define NEKO_MFMA_CUBE_N(LX, SZ) (NEKO_MFMA_SK_N(LX, SZ) * (LX))
348/*
349 * Column stride and size of a staged reference derivative matrix, which takes
350 * the same padding for the same reason.
351 *
352 * D(row, col) = dmat[row + SD*col]. The three divergence contractions read it
353 * transposed, D(l, m), so there the LANE index m walks the column stride --
354 * 16 lanes SD elements apart in the 16x16x4 tile, four in the 4x4x4 one -- and
355 * an even stride puts them on the same banks exactly as it does in the cube.
356 * It is the same policy rather than a second one because it is win or neutral
357 * at every order the policy pads, modelled the same way:
358 *
359 * LX = 8 f64 : 4x4x4 576 -> 384 cycles, 16x16 480 -> 192
360 * LX = 12 f64 : 16x16 1296 -> 648
361 * LX = 4, and both precisions at every unpadded order : unchanged
362 *
363 * A rule of "pad every even order" would also take LX = 10 f64 from 714 to
364 * 504, but costs LX = 10 f32 a 42% increase, which is the same reason the cube
365 * policy stops at multiples of four.
366 */
367#define NEKO_MFMA_SD_N(LX, SZ) NEKO_MFMA_SJ_N(LX, SZ)
368#define NEKO_MFMA_DMAT_N(LX, SZ) (NEKO_MFMA_SD_N(LX, SZ) * (LX))
369
370/*
371 * Whether candidate C's tile is a distinct thing to measure in this build.
372 *
373 * TILE 1 is the 16x16x4 tile, which is what TILE 0 already resolves to under
374 * -DMFMA_F64_USE_16X16 / -DMFMA_F32_USE_16X16, so offering it there would
375 * time the same kernel twice. The launch macros instantiate both regardless
376 * -- every (precision, LX, candidate) has to compile -- and this only decides
377 * what the sweep and the env pin will run.
378 */
379static inline bool mfma_tile_offered(const int c) {
380 if (NEKO_MFMA_TILE(c) == 0) {
381 return true;
382 }
383 if (sizeof(real) == 8) {
384#ifdef MFMA_F64_USE_16X16
385 return false;
386#else
387 return true;
388#endif
389 }
390#ifdef MFMA_F32_USE_16X16
391 return false;
392#else
393 return sizeof(real) == 4;
394#endif
395}
396
397/* Which tile candidate C names, for the tuner log */
398static inline const char *mfma_tile_name(const int c) {
399 if (NEKO_MFMA_TILE(c) != 0) {
400 return "16x16";
401 }
402 if (sizeof(real) == 8) {
403#ifdef MFMA_F64_USE_16X16
404 return "16x16";
405#else
406 return "4x4x4";
407#endif
408 }
409#ifdef MFMA_F32_USE_16X16
410 return "16x16";
411#else
412 return "4x4x1";
413#endif
414}
415
416/*
417 * Column groups per contraction -- the wavefront-parallel work one element
418 * offers. Both tiles group N the same way, 16 columns at a time, so this is
419 * ceil(LX^2/16) either way: 1 at LX = 4, 4 at LX = 8, 9 at LX = 12.
420 *
421 * A wavefront beyond that count has no matrix core work left on that element,
422 * which is what the LX = 4 single precision sweep measured: 20.5 / 23.6 /
423 * 34.0 / 60.2 us as NWF went 1, 2, 4, 8, monotonically worse, against
424 * 218 / 157 / 136.4 / 136.4 at LX = 8 where four groups exist. Rather than
425 * cap the sweep, the surplus wavefronts are given their own element: NWF is
426 * read as wavefronts per block, the block covers EB elements and WPE =
427 * NWF/EB wavefronts cooperate on each. At LX = 4 with eight wavefronts that
428 * is eight elements, one each, with nothing idle; at LX = 12 it is one
429 * element and eight cooperating wavefronts, exactly as before. This matters
430 * because p-multigrid smooths at LX = 4 and 2, so low order Ax is hot rather
431 * than incidental.
432 *
433 * EB is the driving quantity and WPE follows from it, not the other way
434 * round: the block is partitioned into EB equal groups, so EB has to divide
435 * NWF exactly or the leftover wavefronts address an element the block does
436 * not own -- past the end of the shared staging arrays, and past the end of
437 * global storage in the last block. EB = NWF/NGROUPS is therefore rounded
438 * down to a power of two, which divides NWF for every candidate since NWF is
439 * itself 2^C. WPE may then exceed NGROUPS -- at LX = 6, NGROUPS = 3 and four
440 * wavefronts give EB = 1, WPE = 4 -- which is harmless: mfma_contract_4x4()
441 * strides the groups with `ng = wf + gp * NWF` under `ng < NGROUPS`, so a
442 * wavefront without a group of its own simply issues no matrix core work,
443 * while still taking its share of the staging and pointwise passes. The
444 * alternative, capping WPE at NGROUPS and letting EB absorb the remainder,
445 * would grow the shared footprint (LX = 10 with eight wavefronts would want
446 * 66 kB) for no gain.
447 */
448#define NEKO_MFMA_NGROUPS(LX) (((LX) * (LX) + 15) / 16)
449
450/*
451 * LDS a block of EB elements occupies: three reference derivative matrices
452 * shared by the whole block, plus four staged cubes per element. Kept here
453 * rather than in the kernels so that the EB ladder below and the kernels'
454 * own static_assert cannot disagree about what fits.
455 */
456#define NEKO_MFMA_LDS_N(EB, LX, SZ) \
457 ((3 * NEKO_MFMA_DMAT_N(LX, SZ) + 4 * (EB) * NEKO_MFMA_CUBE_N(LX, SZ)) * (SZ))
458#define NEKO_MFMA_LDS_FITS(EB, LX, SZ) \
459 (NEKO_MFMA_LDS_N(EB, LX, SZ) <= NEKO_EB_MAX_LDS)
460
461/* Elements per block: surplus wavefronts, rounded down to a power of two so
462 that WPE * EB == NWF exactly, and clamped to what the 64 kB workgroup LDS
463 limit allows. The LDS clamp is not defensive tidiness: at 16 wavefronts the
464 unclamped ladder asks for EB = 4 at LX = 8 and EB = 2 at LX = 10 and 11 in
465 double precision, all three of which overflow the limit and stop the build
466 in the kernels' static_assert. */
467#define NEKO_MFMA_EB_N(NWF, LX, SZ) \
468 ((NWF) / NEKO_MFMA_NGROUPS(LX) >= 16 && NEKO_MFMA_LDS_FITS(16, LX, SZ) ? 16 :\
469 (NWF) / NEKO_MFMA_NGROUPS(LX) >= 8 && NEKO_MFMA_LDS_FITS(8, LX, SZ) ? 8 : \
470 (NWF) / NEKO_MFMA_NGROUPS(LX) >= 4 && NEKO_MFMA_LDS_FITS(4, LX, SZ) ? 4 : \
471 (NWF) / NEKO_MFMA_NGROUPS(LX) >= 2 && NEKO_MFMA_LDS_FITS(2, LX, SZ) ? 2 : 1)
472#define NEKO_MFMA_EB(LX, C) \
473 NEKO_MFMA_EB_N(NEKO_MFMA_NWF(C), LX, sizeof(real))
474/* Wavefronts cooperating on one element */
475#define NEKO_MFMA_WPE(LX, C) (NEKO_MFMA_NWF(C) / NEKO_MFMA_EB(LX, C))
476
477/*
478 * Elements batched into one instruction stream by the single precision
479 * 4x4x1 tile, see mfma_contract_4x4x1.
480 *
481 * That instruction is sixteen independent 4x4 outer products, so its sixteen
482 * blocks can take 4-column quads from different elements: one element fills
483 * it exactly at LX = 8 (64 columns), four do at LX = 4 and sixteen at LX = 2.
484 * NEL is the largest power of two with NEL * LX^2 <= 64 that still divides
485 * EB -- the batch has to be whole elements of one block, staged in LDS side
486 * by side at a stride of one cube, which is what NEKO_MFMA_EB_N's power of
487 * two ladder guarantees. Only the wavefronts of the first element of each
488 * batch issue the contraction (eb % NEL == 0 at the call sites); the others
489 * idle through it exactly as they do with WPE > NGROUPS. Every other tile
490 * batches nothing: 16x16x4 has one 16-column block, and the double precision
491 * 4x4x4 has four blocks that one LX >= 4 element already fills.
492 */
493#define NEKO_MFMA_POW2_FLOOR(X) \
494 ((X) >= 16 ? 16 : (X) >= 8 ? 8 : (X) >= 4 ? 4 : (X) >= 2 ? 2 : 1)
495#ifdef MFMA_F32_USE_16X16
496#define NEKO_MFMA_NEL_N(EB, LX, SZ, TILE) 1
497#else
498#define NEKO_MFMA_NEL_N(EB, LX, SZ, TILE) \
499 (((SZ) == 4 && (TILE) == 0) ? \
500 ((EB) < NEKO_MFMA_POW2_FLOOR(64 / ((LX) * (LX))) ? \
501 (EB) : NEKO_MFMA_POW2_FLOOR(64 / ((LX) * (LX)))) : 1)
502#endif
503#define NEKO_MFMA_NEL(LX, C) \
504 NEKO_MFMA_NEL_N(NEKO_MFMA_EB(LX, C), LX, sizeof(real), NEKO_MFMA_TILE(C))
505
506/*
507 * Slots per thread: the CUBE_N slots of one staged cube shared out over the
508 * WPE * 64 threads that serve it. Slots rather than points, because the
509 * staging, pointwise and write-back passes walk the LDS cube at unit stride
510 * and skip the pad slots -- see mfma_slot_point(). Where the cube is not
511 * padded a slot is a point and this is the old points-per-thread count.
512 *
513 * Only the vector operator needs this on the host side, but it belongs here
514 * with the rest of the geometry so that the device enum and the host launcher
515 * cannot drift apart -- which is how the WPE/EB split came to address past the
516 * end of its staging arrays.
517 */
518#define NEKO_MFMA_SPT_N(WPE, LX, SZ) \
519 ((NEKO_MFMA_CUBE_N(LX, SZ) + (WPE) * 64 - 1) / ((WPE) * 64))
520#define NEKO_MFMA_SPT(LX, C) \
521 NEKO_MFMA_SPT_N(NEKO_MFMA_WPE(LX, C), LX, sizeof(real))
522
523/*
524 * Whether the seven geometric factors of each of a thread's points are held in
525 * registers, or read from global memory where they are used. The cost is
526 * 7 * SPT * (sizeof(T)/4) VGPRs, budgeted at a quarter of the 256 a lane
527 * addresses -- which leaves the four cube base pointers, the two index bases,
528 * the contraction accumulator and the seven pointwise temporaries room to
529 * live alongside it.
530 *
531 * It buys two different things in the two Ax-helm kernels, and the budget is
532 * the same for both. In the vector kernel (ax_helm_mfma_vector_elem) it is
533 * reuse: the factors are read once for the three components instead of once
534 * each, which is the reason that operator exists at all. In the scalar kernel
535 * (ax_helm_mfma_elem) there is no reuse to be had -- it is latency: the loads
536 * are issued before the gradient contractions instead of behind the barrier
537 * that follows them, so they are in flight across the matrix core work rather
538 * than exposed after it.
539 *
540 * It follows from LX and the wavefront count rather than being a candidate of
541 * its own, so the two modes cannot be compared directly -- which is why the
542 * tuner reports which one each candidate ran, see NEKO_TUNE_LOG_MFMA_VEC.
543 * Without that the sweep reads as a matrix core result when the step between
544 * two candidates is really a change in memory traffic.
545 */
546#ifndef NEKO_MFMA_VECTOR_GREG_VGPRS
547#define NEKO_MFMA_VECTOR_GREG_VGPRS 64
548#endif
549#define NEKO_MFMA_VECTOR_GREG_N(SPT, SZ) \
550 ((7 * (SPT) * ((SZ) / 4)) <= NEKO_MFMA_VECTOR_GREG_VGPRS)
551#define NEKO_MFMA_VECTOR_GREG(LX, C) \
552 NEKO_MFMA_VECTOR_GREG_N(NEKO_MFMA_SPT(LX, C), sizeof(real))
553
554#define NEKO_MFMA_NBLCKS(NELV, LX, C) \
555 dim3(((NELV) + NEKO_MFMA_EB(LX, C) - 1) / NEKO_MFMA_EB(LX, C), 1, 1)
556
557/*
558 * Whether the autotuner sweeps the MFMA strategy, on by default wherever the
559 * hardware and the polynomial order allow it.
560 *
561 * It was briefly off while the matrix core contraction was known broken -- the
562 * lane layout had the block selector and the contraction index interchanged,
563 * which a "the solver converges" check had failed to catch for a long time.
564 * The layout was measured on gfx90a, corrected, and mfma_contract_4x4 now
565 * reproduces a CPU reference to ~1e-16 over every supported order, axis,
566 * transpose/accumulate mode and wavefront count, so there is no reason to
567 * withhold it from the sweep. Kept as an off switch in the shape of
568 * NEKO_EB_TUNE, for A/B work.
569 */
570static int neko_mfma_sweep()
571{
572 const char *v = getenv("NEKO_MFMA_TUNE");
573
574 if (v != NULL) {
575 return (atoi(v) != 0);
576 }
577 return 1;
578}
579
580/*
581 * Candidate pinned by NEKO_MFMA_NWF and NEKO_MFMA_TILE, or -1 to leave it to
582 * the sweep, see neko_eb_pin() in elem_block_tune.h.
583 *
584 * The two dimensions stay separate variables -- NEKO_MFMA_NWF keeps its old
585 * range and meaning, NEKO_MFMA_TILE adds the tile -- rather than one index
586 * into the combined space, so that a pin written before the tile existed
587 * still selects what it used to. Setting either one pins the candidate;
588 * leaving both unset leaves the whole geometry to the sweep. A tile this
589 * build does not offer falls back to the default one at the requested
590 * wavefront count.
591 */
592static int neko_mfma_pin()
593{
594 const char *v = getenv("NEKO_MFMA_NWF");
595 const char *t = getenv("NEKO_MFMA_TILE");
596 int nwf, tile, c;
597
598 if (v == NULL && t == NULL) {
599 return -1;
600 }
601
602 nwf = (v != NULL) ? atoi(v) : 0;
603 tile = (t != NULL) ? atoi(t) : 0;
605 nwf = 0;
606 }
608 tile = 0;
609 }
611 if (!mfma_tile_offered(c)) {
612 c = nwf;
613 }
614 return c;
615}
616
617/*
618 * Candidates the sweep runs: the wavefront counts on the default tile, and
619 * the same again on the 16x16x4 tile wherever this build offers that as a
620 * distinct thing to measure, see mfma_tile_offered(). The offered set is
621 * contiguous from 0, so it is a count rather than a skip and NEKO_TUNE_FOR()
622 * in elem_block_tune.h can take it directly.
623 */
629
630/* Report every measured MFMA candidate, see NEKO_TUNE_LOG in
631 elem_block_tune.h. The tile is named because it is the dimension whose
632 default was wrong for eight of the nine supported orders */
633#define NEKO_TUNE_LOG_MFMA(LX, T3) \
634 do { \
635 for (int c = 0; c < NEKO_MFMA_CANDIDATES; c++) { \
636 if ((T3)[c] >= NEKO_TUNE_INIT) { continue; } \
637 sprintf(neko_log_buf, "MFMA %s %2dwf %-2de x%-2d %-5s: %9.2f us/call", \
638 mfma_tile_name(c), NEKO_MFMA_NWF(c), NEKO_MFMA_EB(LX, c), \
639 (int) NEKO_MFMA_NEL(LX, c), \
640 NEKO_MFMA_PAD_N(LX, sizeof(real)) ? "pad" : "plain", \
641 NEKO_TUNE_US((T3)[c], iters)); \
642 log_message(neko_log_buf); \
643 } \
644 } while (0)
645
646/*
647 * The same for the vector operator, plus the register mode each candidate
648 * ran in -- 'reg' where the geometric factors stay in registers across the
649 * three components, 'glob' where they are re-read from global memory for each
650 * one, see NEKO_MFMA_VECTOR_GREG. It is reported because it is derived from
651 * the wavefront count rather than swept, so two neighbouring candidates can
652 * differ in memory traffic as well as in block shape, and a step between them
653 * would otherwise be read as a matrix core effect.
654 */
655#define NEKO_TUNE_LOG_MFMA_VEC(LX, T3) \
656 do { \
657 for (int c = 0; c < NEKO_MFMA_CANDIDATES; c++) { \
658 if ((T3)[c] >= NEKO_TUNE_INIT) { continue; } \
659 sprintf(neko_log_buf, \
660 "MFMA %s %2dwf %-2de x%-2d %-5s %-4s: %9.2f us/call", \
661 mfma_tile_name(c), NEKO_MFMA_NWF(c), NEKO_MFMA_EB(LX, c), \
662 (int) NEKO_MFMA_NEL(LX, c), \
663 NEKO_MFMA_PAD_N(LX, sizeof(real)) ? "pad" : "plain", \
664 NEKO_MFMA_VECTOR_GREG(LX, c) ? "reg" : "glob", \
665 NEKO_TUNE_US((T3)[c], iters)); \
666 log_message(neko_log_buf); \
667 } \
668 } while (0)
669
670#if defined(__gfx90a__) || defined(__gfx942__)
671
672/* 4-wide accumulators for the gfx90a / gfx942 matrix cores. */
673typedef double mfma_f64x4 __attribute__((ext_vector_type(4)));
674typedef float mfma_f32x4 __attribute__((ext_vector_type(4)));
675
676/*
677 * Per-precision matrix-core traits: the 4-wide accumulator type, the MFMA
678 * builtin, and the accumulator-slot -> output-row mapping (see the layout
679 * note above; f64 spreads rows with stride 4, f32 packs four contiguous rows).
680 */
681template< typename T >
682struct mfma_traits;
683
684template< >
685struct mfma_traits< double > {
686 typedef mfma_f64x4 acc_t;
687 __device__ __forceinline__ static acc_t mma(double a, double b, acc_t c) {
688 return __builtin_amdgcn_mfma_f64_16x16x4f64(a, b, c, 0, 0, 0);
689 }
690 __device__ __forceinline__ static int out_row(const int g, const int r) {
691 return g + 4 * r;
692 }
693};
694
695template< >
696struct mfma_traits< float > {
697 typedef mfma_f32x4 acc_t;
698 __device__ __forceinline__ static acc_t mma(float a, float b, acc_t c) {
699 return __builtin_amdgcn_mfma_f32_16x16x4f32(a, b, c, 0, 0, 0);
700 }
701 __device__ __forceinline__ static int out_row(const int g, const int r) {
702 return 4 * g + r;
703 }
704};
705
706/*
707 * Storage layout of one staged cube: i + SJ*j + SK*k over SIZE slots, with the
708 * j-stride padded to an odd number of elements where that removes the LDS bank
709 * conflicts -- see the padding note above for the model and the policy.
710 *
711 * PAD is carried as a template parameter rather than read from the macro so
712 * that the operators which stage a plain LX^3 cube (dudxyz, opgrad, conv1,
713 * cdtp) keep the layout they were written for: the default is 0 and only
714 * Ax-helm opts in.
715 */
716template< const int LX, const int PAD >
717struct mfma_cube {
718 enum { SJ = LX + PAD,
719 SK = (LX + PAD) * LX,
720 SIZE = (LX + PAD) * LX * LX,
721 /* column stride of a staged derivative matrix, padded with the cube
722 and for the same reason -- see the note on NEKO_MFMA_SD_N */
723 SD = LX + PAD,
724 DSIZE = (LX + PAD) * LX };
725};
726
727/*
728 * Offset of D(row, col) in a staged derivative matrix. The caller's copy is
729 * LX x LX with a column stride of LX; this is where it lives once staged.
730 */
731template< const int LX, const int PAD >
732__device__ __forceinline__ int mfma_dmat_idx(const int row, const int col) {
733 return row + mfma_cube<LX, PAD>::SD * col;
734}
735
736/*
737 * Linearised index into a staged cube, where the coordinate 'p' lies on
738 * contraction axis AXIS and 'n' enumerates the two remaining axes as
739 * n = a + LX*b.
740 */
741template< const int LX, const int AXIS, const int PAD = 0 >
742__device__ __forceinline__ int mfma_cube_idx(const int p, const int n) {
743 typedef mfma_cube<LX, PAD> C;
744 const int a = n % LX;
745 const int b = n / LX;
746 if (AXIS == 0) return p + C::SJ * a + C::SK * b; // contract i; n = (j,k)
747 if (AXIS == 1) return a + C::SJ * p + C::SK * b; // contract j; n = (i,k)
748 return a + C::SJ * b + C::SK * p; // contract k; n = (i,j)
749}
750
751/*
752 * The point of the element that LDS slot 's' holds, as an offset into the
753 * caller's LX^3 global storage, or -1 for a pad slot that holds nothing.
754 *
755 * The staging, pointwise and write-back passes walk SLOTS at unit stride and
756 * translate here, rather than walking points and translating the other way.
757 * Both directions are correct; this one is the one that keeps those passes
758 * conflict free. Walking points would leave the LDS side striding across the
759 * padding -- a 2-way conflict on every pass, which the bank model prices at
760 * more than the contraction gains back at LX = 6 and 10 -- while walking slots
761 * leaves the GLOBAL side with a hole every SJ lanes, which costs nothing: the
762 * lanes either side of the hole still touch the same cache lines, the hole
763 * lane simply takes no part.
764 *
765 * The division is by a compile-time constant and is meant to be hoisted: each
766 * kernel decodes its own slots once into a small register array and reuses it
767 * across all three passes.
768 */
769template< const int LX, const int PAD >
771 typedef mfma_cube<LX, PAD> C;
772 if (PAD == 0) {
773 return s;
774 }
775 const int k = s / C::SK;
776 const int rem = s - k * C::SK;
777 const int j = rem / C::SJ;
778 const int i = rem - j * C::SJ;
779 /* j < LX holds by construction (rem < SK = SJ*LX), i and k do not */
780 return (i < LX && k < LX) ? (i + LX * j + LX * LX * k) : -1;
781}
782
783/*
784 * One wavefront contracts the reference derivative matrix 'dmat' (LX x LX,
785 * stored column-major: D(row,col) = dmat[row + LX*col]) with the cube 'in'
786 * along axis AXIS, writing the cube 'out':
787 *
788 * out[idx(m,n)] (+)= sum_l D(m,l) * in[idx(l,n)] (TRANSPOSE = false)
789 * out[idx(m,n)] (+)= sum_l D(l,m) * in[idx(l,n)] (TRANSPOSE = true)
790 *
791 * GEMM dimensions M = LX, N = LX*LX, K = LX, tiled over 16x16x4 MFMA tiles
792 * with the partial M/N/K tiles masked. ACCUM selects += over = .
793 */
794/*
795 * NWF cooperating wavefronts (wf = 0..NWF-1) stripe the NTILES N-tiles among
796 * themselves -- wavefront wf handles nt = wf, wf+NWF, ... NWF = 1, wf = 0
797 * (the defaults) reproduces the single-wavefront contraction.
798 */
799template< typename T, const int LX, const int AXIS,
800 const bool TRANSPOSE, const bool ACCUM, const int NWF = 1,
801 const int PAD = 0 >
804 const T * __restrict__ dmat,
805 const T * __restrict__ in,
806 const int lane, const int wf = 0) {
807 typedef mfma_traits<T> mma_t;
808 const int g = lane >> 4; // lane / 16 -> 0..3
809 const int c = lane & 15; // lane % 16 -> 0..15
810 const int NTILES = (LX * LX + 15) / 16;
811 const int KSTEPS = (LX + 3) / 4;
812 const int NPASS = (NTILES + NWF - 1) / NWF; // N-tiles handled by this wave
813
814 /* A operand, hoisted: this lane's D(c, l) for every K-step does not depend
815 on the N-tile, so it is read once per contraction rather than once per
816 tile. KSTEPS <= 3 registers, the same values in the same order. */
817 T areg[KSTEPS];
818#pragma unroll
819 for (int ks = 0; ks < KSTEPS; ++ks) {
820 const int l = ks * 4 + g; // contraction index
821 areg[ks] = 0;
822 if (c < LX && l < LX)
823 areg[ks] = TRANSPOSE ? dmat[mfma_dmat_idx<LX, PAD>(l, c)] // D(l,c)
824 : dmat[mfma_dmat_idx<LX, PAD>(c, l)]; // D(c,l)
825 }
826
827#pragma unroll
828 for (int p = 0; p < NPASS; ++p) {
829 const int nt = wf + p * NWF; // this wavefront's N-tile
830 if (nt < NTILES) {
831 const int n = nt * 16 + c; // free (column) index
832 typename mma_t::acc_t acc = {0, 0, 0, 0};
833#pragma unroll
834 for (int ks = 0; ks < KSTEPS; ++ks) {
835 const int l = ks * 4 + g; // contraction index
836 T b = 0;
837 if (l < LX && n < LX * LX)
839 acc = mma_t::mma(areg[ks], b, acc);
840 }
841 const T dvals[4] = { acc[0], acc[1], acc[2], acc[3] };
842#pragma unroll
843 for (int r = 0; r < 4; ++r) {
844 const int m = mma_t::out_row(g, r); // output coordinate on AXIS
845 if (m < LX && n < LX * LX) {
846 const int idx = mfma_cube_idx<LX, AXIS, PAD>(m, n);
847 if (ACCUM) out[idx] += dvals[r];
848 else out[idx] = dvals[r];
849 }
850 }
851 }
852 }
853}
854
855/*
856 * Double-precision batched matrix-core contraction using v_mfma_f64_4x4x4f64.
857 *
858 * Same contract as mfma_contract() -- out(m,n) (+)= sum_l D(m,l) in(l,n), with
859 * D(l,m) when TRANSPOSE -- but tiled with 4x4x4 MFMA tiles and the
860 * instruction's four blocks assigned to four consecutive 4-column N-subtiles.
861 * GEMM M = LX, N = LX*LX, K = LX, tiled as MT = ceil(LX/4) M-tiles,
862 * KSTEPS = ceil(LX/4) K-steps and NGROUPS = ceil(LX^2/16) column groups (each
863 * group = 4 blocks x 4 columns); partial M/N/K masked with zeros.
864 *
865 * Versus the 16x16x4 tile, the 4-wide M granularity fills M = LX < 16 exactly
866 * (LX = 8 runs the matrix core at 100% M-utilisation instead of 50%), at the
867 * cost of 4x as many, 1/4-sized MFMA issues that feed the same f64 matrix
868 * pipeline. Double precision only: the f32 4x4 instruction has K = 1, so there
869 * is no single-precision counterpart -- single precision keeps the 16x16x4 tile
870 * (see mfma_contract_sel below).
871 *
872 * Wave of 64 lanes = 4 blocks x 16 lanes. v_mfma_f64_4x4x4f64 lane layout,
873 * with lo = lane%4, gemm = (lane/4)%4 and kq = lane/16:
874 * A[i][k] : lane holds A[i = lo ][k = kq]
875 * B[k][j] : lane holds B[k = kq ][j = lo]
876 * D[i][j] : lane holds D[i = kq ][j = lo] (scalar f64 accumulator)
877 * and the four independent 4x4x4 blocks are selected by 'gemm', not by
878 * lane/16.
879 *
880 * This was MEASURED on gfx90a, not assumed: a one-hot read-out of which lane
881 * receives which element (128 launches, no assumptions) produced exactly this
882 * mapping. The previous version had 'gemm' and 'kq' interchanged -- it used
883 * lane/16 as the block selector and (lane/4)%4 as the contraction index -- and
884 * every one of 72 probe configurations disagreed with a CPU reference.
885 *
886 * It had been believed validated because the Ax-helm fluid solver converged
887 * with it. It does: a wrong but symmetric operator makes CG converge happily
888 * to the solution of a different system. Convergence is not correctness, and
889 * only a diff against a reference settles a layout. Any future correction
890 * stays localised to the four index expressions (m_a, l, n, m_d).
891 *
892 * NWF cooperating wavefronts (wf = 0..NWF-1) stripe the NGROUPS column groups
893 * among themselves -- wavefront wf handles ng = wf, wf+NWF, ... NWF = 1,
894 * wf = 0 (the defaults) reproduces the single-wavefront contraction.
895 */
896template< const int LX, const int AXIS,
897 const bool TRANSPOSE, const bool ACCUM, const int NWF = 1,
898 const int PAD = 0 >
901 const double * __restrict__ dmat,
902 const double * __restrict__ in,
903 const int lane, const int wf = 0) {
904 const int lo = lane & 3; // 0..3 : A row, B column, D column
905 const int gemm = (lane >> 2) & 3; // 0..3 : which of the four 4x4x4 blocks
906 const int kq = lane >> 4; // 0..3 : contraction index within a step
907 const int MT = (LX + 3) / 4;
908 const int KSTEPS = (LX + 3) / 4;
909 const int NGROUPS = (LX * LX + 15) / 16;
910 const int NPASS = (NGROUPS + NWF - 1) / NWF; // groups handled by this wave
911
912 /*
913 * M-tile innermost, with one accumulator per M-tile live across the K loop.
914 *
915 * The obvious nest is M-tile outermost, one accumulator, and that is what
916 * this was. It re-reads the whole B operand once per M-tile -- B depends on
917 * (group, K-step) and not on the M-tile at all -- which costs 2x the LDS
918 * operand traffic at LX = 5..8 and 3x at LX = 9..12, on a kernel that
919 * measures at 69-72% of the memory roof. The bank model puts the B read at
920 * 1408 of the 2400 LDS cycles an LX = 8 f64 element spends, and hoisting it
921 * out takes that to 704.
922 *
923 * It also breaks up the accumulate chain. AMD documents a four cycle wait on
924 * a V_MFMA_4x4x4_F64 -> V_MFMA_4x4x4_F64 SrcC dependency, and the single
925 * accumulator made the K loop exactly that chain; consecutive issues now
926 * write different accumulators wherever MT > 1, which is every order above
927 * four, so the waits overlap instead of adding up.
928 *
929 * Each accumulator still sums over ks in the same order it did, so the
930 * results are bit-identical to the previous nest, not merely equivalent.
931 */
932#pragma unroll
933 for (int gp = 0; gp < NPASS; ++gp) {
934 const int ng = wf + gp * NWF; // this wavefront's column group
935 if (ng < NGROUPS) {
936 /* the four blocks take four consecutive 4-column N-subtiles */
937 const int n = ng * 16 + gemm * 4 + lo; // column (N) for B in / D out
938 double acc[MT];
939#pragma unroll
940 for (int mt = 0; mt < MT; ++mt)
941 acc[mt] = 0.0;
942#pragma unroll
943 for (int ks = 0; ks < KSTEPS; ++ks) {
944 const int l = ks * 4 + kq; // contraction index (k = kq for A and B)
945 double b = 0.0;
946 if (l < LX && n < LX * LX)
948#pragma unroll
949 for (int mt = 0; mt < MT; ++mt) {
950 const int m_a = mt * 4 + lo; // A row (input lane layout: i = lo)
951 double a = 0.0;
952 if (m_a < LX && l < LX)
953 a = TRANSPOSE ? dmat[mfma_dmat_idx<LX, PAD>(l, m_a)] // D(l,m)
954 : dmat[mfma_dmat_idx<LX, PAD>(m_a, l)]; // D(m,l)
956 }
957 }
958#pragma unroll
959 for (int mt = 0; mt < MT; ++mt) {
960 const int m_d = mt * 4 + kq; // D row (output lane layout: i = kq)
961 if (m_d < LX && n < LX * LX) {
962 const int idx = mfma_cube_idx<LX, AXIS, PAD>(m_d, n);
963 if (ACCUM) out[idx] += acc[mt];
964 else out[idx] = acc[mt];
965 }
966 }
967 }
968 }
969}
970
971/*
972 * Single-precision batched matrix-core contraction using v_mfma_f32_4x4x1f32.
973 *
974 * Same contract as mfma_contract() -- out(m,n) (+)= sum_l D(m,l) in(l,n), with
975 * D(l,m) when TRANSPOSE -- for NEL elements whose cubes sit side by side at a
976 * stride of one cube (mfma_cube<LX, PAD>::SIZE) behind 'in' and 'out', so the
977 * GEMM is M = LX, N = NEL * LX^2, K = LX.
978 *
979 * The instruction is sixteen independent 4x4 outer products (K = 1) in 8
980 * cycles, at the same 256 flop per clock per CU as every other f32 MFMA on
981 * gfx90a / gfx942, so a full tile of it costs a quarter of the cycles of a
982 * 16x16x4 tile for the same work. Its M granularity of four fills LX < 16
983 * where the 16x16x4 tile wastes 12 of 16 rows at LX = 4 and half at LX = 8,
984 * its K = 1 has no K waste at any order, and its sixteen blocks take sixteen
985 * 4-column quads of the batch's columns, so N is filled to within a quad.
986 * Counting issues per element per contraction against the 16x16x4 tile it
987 * saves 2x at LX = 8, 4x at LX = 4 with NEL = 4 and 32x at LX = 2 with
988 * NEL = 16; see NEKO_MFMA_NEL_N.
989 *
990 * Nest: the A operand -- D(m, l) for this lane's rows m = 4*mt + lo, every l
991 * -- is register resident for the whole call, MT * LX values, and the B
992 * operand is read once per (column group, l) and shared by the MT M-tiles,
993 * which keep MT independent accumulator chains. On gfx90a a back-to-back
994 * same-accumulator f32 chain needs no wait states at all (LLVM's hazard
995 * tables give the f64 4x4x4 chain four, and gfx940's two-pass f32 tiles two),
996 * so the chain is free here where mfma_contract_4x4's is not.
997 *
998 * Wave of 64 lanes = 16 blocks x 4 lanes, blk = lane/4, lo = lane%4. Lane
999 * layout, from AMD's matrix instruction calculator for CDNA2, no modifiers:
1000 * A[i][0].block : lane 4*blk + i -> lane supplies D(4*mt + lo, l)
1001 * B[0][j].block : lane 4*blk + j -> lane supplies in(l, n),
1002 * n = 64*ng + 4*blk + lo
1003 * D[i][j].block : lane 4*blk + j, register i -> lane holds out(4*mt + r, n)
1004 * in accumulator slot r
1005 * All sixteen blocks share the same A rows (one M-tile of D), so the CBSZ /
1006 * ABID broadcast could feed them from one block's lanes; it is not used,
1007 * every lane simply carries the row its block needs, which costs nothing and
1008 * keeps the modifiers at zero.
1009 *
1010 * DOCUMENTED, NOT MEASURED. This layout has not been read out on hardware
1011 * the way the 4x4x4 one was. The same tool reproduces the 4x4x4 f64 layout
1012 * that WAS measured (A at 4*block + 16*k + i, D at 16*i + 4*block + j, the
1013 * mapping mfma_contract_4x4 carries), which is why it is trusted this far and
1014 * no further: the tuner's verification pass compares every candidate with
1015 * the reference formulation on a synthetic input before timing it and
1016 * rejects a mismatch (NEKO_TUNE_VERIFY, elem_block_tune.h), so a wrong
1017 * register index here costs the candidate, not the solve. If the f32 TILE 0
1018 * candidates are all rejected on a part, -DMFMA_F32_USE_16X16 restores the
1019 * measured 16x16x4 path, and the one-hot read-out that settled the 4x4x4
1020 * layout is the way to settle this one.
1021 *
1022 * NWF cooperating wavefronts stripe the 64-column groups, NG = ceil(NEL *
1023 * LX^2 / 64) of them: one at LX <= 8, which is the whole contraction on one
1024 * wavefront with the others idle, two at LX = 9..11, three at LX = 12.
1025 */
1026template< const int LX, const int AXIS,
1027 const bool TRANSPOSE, const bool ACCUM, const int NWF = 1,
1028 const int PAD = 0, const int NEL = 1 >
1031 const float * __restrict__ dmat,
1032 const float * __restrict__ in,
1033 const int lane, const int wf = 0) {
1034 typedef mfma_cube<LX, PAD> C;
1035 const int blk = lane >> 2; // 0..15 : which of the sixteen blocks
1036 const int lo = lane & 3; // 0..3 : A row, B column, D column
1037 const int MT = (LX + 3) / 4; // M-tiles of four rows
1038 const int LX2 = LX * LX;
1039 const int NCOL = NEL * LX2; // columns across the batch
1040 const int NG = (NCOL + 63) / 64; // 64-column groups
1041 const int NPASS = (NG + NWF - 1) / NWF; // groups handled by this wave
1042 const mfma_f32x4 zero = {0.0f, 0.0f, 0.0f, 0.0f};
1043
1044 /* A operand: D(m, l) -- D(l, m) when TRANSPOSE -- for this lane's rows,
1045 every l, zero past M = LX */
1046 float areg[MT][LX];
1047#pragma unroll
1048 for (int mt = 0; mt < MT; ++mt) {
1049 const int m_a = mt * 4 + lo; // A row (input lane layout: i = lo)
1050#pragma unroll
1051 for (int l = 0; l < LX; ++l) {
1052 areg[mt][l] = 0.0f;
1053 if (m_a < LX)
1056 }
1057 }
1058
1059#pragma unroll
1060 for (int gp = 0; gp < NPASS; ++gp) {
1061 const int ng = wf + gp * NWF; // this wavefront's column group
1062 if (ng < NG) {
1063 const int ncol = ng * 64 + 4 * blk + lo; // column across the batch
1064 const bool live = (ncol < NCOL);
1065 const int q = live ? (ncol / LX2) : 0; // element within the batch
1066 const int n = ncol - q * LX2; // column within that element
1067 const float * __restrict__ inq = in + q * C::SIZE;
1068 float * __restrict__ outq = out + q * C::SIZE;
1069 mfma_f32x4 acc[MT];
1070#pragma unroll
1071 for (int mt = 0; mt < MT; ++mt)
1072 acc[mt] = zero;
1073#pragma unroll
1074 for (int l = 0; l < LX; ++l) {
1075 float b = 0.0f;
1076 if (live)
1078#pragma unroll
1079 for (int mt = 0; mt < MT; ++mt)
1081 acc[mt], 0, 0, 0);
1082 }
1083#pragma unroll
1084 for (int mt = 0; mt < MT; ++mt) {
1085#pragma unroll
1086 for (int r = 0; r < 4; ++r) {
1087 const int m_d = mt * 4 + r; // D row (output layout: register r)
1088 if (live && m_d < LX) {
1089 const int idx = mfma_cube_idx<LX, AXIS, PAD>(m_d, n);
1090 if (ACCUM) outq[idx] += acc[mt][r];
1091 else outq[idx] = acc[mt][r];
1092 }
1093 }
1094 }
1095 }
1096 }
1097}
1098
1099/*
1100 * Precision- and tile-dispatched tensor contraction, so that one call site
1101 * covers both precisions and both matrix core tiles.
1102 *
1103 * TILE 0 is the precision's batched tile: the 4x4x4 in double precision
1104 * (full M-utilisation for M = LX < 16, at half the FLOP rate -- see the
1105 * candidate space note above for why that trade is worth measuring), the
1106 * 4x4x1 in single (full M-utilisation at the full rate, and NEL elements to
1107 * an instruction at low order). TILE 1 is the 16x16x4 tile in both.
1108 *
1109 * -DMFMA_F64_USE_16X16 / -DMFMA_F32_USE_16X16 make TILE 0 the 16x16x4 tile in
1110 * that precision as well, which collapses the tile dimension; the sweep then
1111 * offers TILE 0 alone, see mfma_tile_offered(). They are retained as a way to
1112 * build without the batched paths at all, not as the way to choose between
1113 * them -- that is the autotuner's job. In single precision it is also the
1114 * fallback should the documented 4x4x1 layout be rejected on a part, see
1115 * mfma_contract_4x4x1.
1116 *
1117 * NEL is the number of elements the call covers, see NEKO_MFMA_NEL_N. Only
1118 * the single precision batched tile takes NEL > 1; the other dispatches
1119 * static_assert it away rather than looping, because the call sites derive
1120 * NEL from the same macro and can never ask for it.
1121 *
1122 * The double precision tiles and the single precision 16x16x4 one are
1123 * hardware verified against a CPU reference on gfx90a (mfma_probe, 144
1124 * configurations per tile); the double precision tiles give bit-identical
1125 * results, so which one runs is a performance question only.
1126 */
1127template< typename T, const int LX, const int AXIS,
1128 const bool TRANSPOSE, const bool ACCUM, const int NWF = 1,
1129 const int TILE = 0, const int PAD = 0, const int NEL = 1 >
1130struct mfma_contract_sel {
1131 static_assert(NEL == 1,
1132 "only the single precision 4x4x1 tile batches elements");
1134 static void run(T * __restrict__ out, const T * __restrict__ dmat,
1135 const T * __restrict__ in, const int lane,
1136 const int wf = 0) {
1138 lane, wf);
1139 }
1140};
1141
1142/* Double precision, TILE 0: the batched 4x4x4 tile, or the 16x16x4 one if the
1143 build asked for it */
1144template< const int LX, const int AXIS, const bool TRANSPOSE, const bool ACCUM,
1145 const int NWF, const int PAD, const int NEL >
1147 static_assert(NEL == 1,
1148 "only the single precision 4x4x1 tile batches elements");
1150 static void run(double * __restrict__ out, const double * __restrict__ dmat,
1151 const double * __restrict__ in, const int lane,
1152 const int wf = 0) {
1153#ifdef MFMA_F64_USE_16X16
1155 lane, wf);
1156#else
1158 lane, wf);
1159#endif
1160 }
1161};
1162
1163/* Double precision, TILE 1: the 16x16x4 tile, M-utilisation 50% at LX = 8 and
1164 75% at LX = 12, but at twice the FLOP rate of the batched tile, a free
1165 accumulate chain and a third of its operand traffic at high order */
1166template< const int LX, const int AXIS, const bool TRANSPOSE, const bool ACCUM,
1167 const int NWF, const int PAD, const int NEL >
1169 static_assert(NEL == 1,
1170 "only the single precision 4x4x1 tile batches elements");
1172 static void run(double * __restrict__ out, const double * __restrict__ dmat,
1173 const double * __restrict__ in, const int lane,
1174 const int wf = 0) {
1176 lane, wf);
1177 }
1178};
1179
1180/* Single precision, TILE 0: the batched 4x4x1 tile with NEL elements to a
1181 call, or the 16x16x4 one if the build asked for it. TILE 1 is the primary
1182 template above. */
1183template< const int LX, const int AXIS, const bool TRANSPOSE, const bool ACCUM,
1184 const int NWF, const int PAD, const int NEL >
1187 static void run(float * __restrict__ out, const float * __restrict__ dmat,
1188 const float * __restrict__ in, const int lane,
1189 const int wf = 0) {
1190#ifdef MFMA_F32_USE_16X16
1191 static_assert(NEL == 1,
1192 "NEKO_MFMA_NEL_N is 1 under MFMA_F32_USE_16X16");
1194 lane, wf);
1195#else
1197 in, lane,
1198 wf);
1199#endif
1200 }
1201};
1202
1203#endif // __gfx90a__ || __gfx942__
1204#endif // __MATH_MFMA_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 int i
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ v
const int j
double real
#define HIP_CHECK(err)
Definition check.h:8
static __global__ void hip_mfma_arch_probe(int *flag)
static bool mfma_lx_supported()
static const char * mfma_tile_name(const int c)
#define NEKO_MFMA_NWF_CANDIDATES
#define NEKO_MFMA_CANDIDATES
static int neko_mfma_pin()
static int neko_mfma_candidates()
#define NEKO_MFMA_TILE_CANDIDATES
static bool hip_have_mfma()
#define NEKO_MFMA_TILE(C)
static int neko_mfma_sweep()
static bool mfma_tile_offered(const int c)