hipfire
/learn · silicon · CDNA / MFMA

CDNA & MFMA

Companion to RDNA & WMMA. This page walks the data-center half of the AMD compute family: the Instinct lineage, the v_mfma_* matrix engine, the HBM memory hierarchy, the roofline that decides whether your kernel is memory-bound or compute-bound, and the rocprofv3 counters that answer that question.

hipfire is RDNA-first: its tuned kernels target gfx11 and gfx12. CDNA runs through a portable wave64 path, with MFMA reached through rocBLAS on CDNA3 (§ 18). The rest of this page is the hardware picture you need to read that path: wave64, MFMA tiles, HBM, and a transistor budget with no graphics pipeline.


01 The roofline model

Two hardware ceilings — compute throughput and memory bandwidth — place an upper bound on any kernel. Arithmetic intensity (FLOPs per byte) decides which one you hit.

fig · roofline, MI300X class
arithmetic intensity (FLOPs / byte) → performance (FLOPs/s) → HBM3 BW roof peak compute roof (MFMA) ridge MEMORY-BOUND COMPUTE-BOUND SAXPY (O(1)) FFT (O(log N)) SGEMM (O(N))
  • AI = FLOPs ÷ bytes transferred. Left of the ridge = memory-bound → optimize data reuse. Right of the ridge = compute-bound → optimize ALU use.
  • Algorithm AI scaling: SAXPY O(1) · FFT O(log N) · SGEMM O(N).
  • The roofline is the only diagnostic that tells you which optimization to try first. Profile, plot, then tune.

02 Profiling — which metrics, and why

rocprofv3 collects hundreds of counters. Three families diagnose a kernel: timing, compute, memory.

rocprofv3 --kernel-trace \
  --pmc SQ_WAVES VALUBusy MemUnitBusy FetchSize \
  -- ./my_kernel
# writes  <pid>_kernel_trace.csv          (timing)
#         <pid>_counter_collection.csv    (counters)
familycounterswhat it tells you
timing Start_Timestamp, End_Timestamp, Kernel_Name, VGPR_Count Which kernel runs longest, and is its occupancy register-limited?
compute VALUBusy, VALUUtilization, SQ_WAVES Low VALUBusy → CU idle → memory- or latency-bound. Low VALUUtilization → divergence.
memory MemUnitBusy, L2CacheHit, FetchSize High MemUnitBusy + low L2CacheHit → hitting HBM not cache. Poor coalescing or no reuse.

03 ROCm — the software stack underneath

ROCm = Radeon Open Compute — AMD's open-source software stack for GPU computing.

what ships ( + )
  • Open source. Entire stack on GitHub. No vendor lock-in — inspect, patch, or fork any layer you depend on.
  • CUDA compatible. HIP mirrors the CUDA runtime API. hipify automates most of the port rewrite.
core SDK groups
  • Frameworks: JAX, ONNX-RT, PyTorch, TensorFlow.
  • ML / CV: CK, MIGraphX, MIOpen, MIVisionX, RPP, rocAL, rocDecode, rocJPEG.
  • Comm: RCCL, rocSHMEM.
  • Math: hipBLAS, hipBLASLt, hipFFT, hipRAND, hipSOLVER, hipSPARSE, rocBLAS, rocFFT, rocSOLVER, rocWMMA, Tensile, hipfort, half.
  • Primitives: hipCUB, hipTensor, rocPRIM, rocThrust.
  • Tools: AMD SMI, ROCm SMI, rocminfo, rocprofv3, Compute Profiler, HIPIFY, ROCgdb.
  • Compilers: HIPCC, ROCm compilers, FLANG.
  • Runtime: HIP runtime.

04 ROCm releases & compatibility

Which product runs which architecture, and how that maps to an LLVM target.

productarchitectureLLVM target
MI355X · MI350XCDNA 4gfx950
MI325X · MI300X · MI300ACDNA 3gfx942
MI250X · MI250 · MI210CDNA 2gfx90a
MI100CDNA 1gfx908
RX 9070 / 9060 · PRO R9700RDNA 4gfx1200 / 1201
RX 7900 / 7800 / 7700 · PRO W7900 / W7800RDNA 3gfx1100 / 1101
Ryzen AI / iGPURDNA 3.5gfx1150 / 1151

05 HIP — one source, two targets

Compiler that targets both AMD and NVIDIA GPUs from a single source file.

fig · HIP compilation flow
                       my_kernel.hip
                            │
                          hipcc
                       ┌────┴────┐
                       ▼         ▼
                   amdclang     nvcc
                       │         │
                       ▼         ▼
                   AMD ISA    NVIDIA PTX
        
  • Same source: same .hip compiles on either platform — no #ifdefs, no per-vendor branches.
  • CUDA-like syntax: __global__, <<<grid,block>>>, threadIdx, blockIdx.
  • Most CUDA code ports: hipify-perl renames CUDA → HIP automatically.

06 HIP vs CUDA — side by side

Minimal vector-add kernel in both. The shape is identical — only the API prefix differs.

HIP (AMD)
#include <hip/hip_runtime.h>
__global__ void add(float* a, float* b, float* c) {
  int i = blockIdx.x * blockDim.x + threadIdx.x;
  c[i] = a[i] + b[i];
}
int main() {
  float *a, *b, *c;
  hipMalloc(&a, N*sizeof(float));
  hipMalloc(&b, N*sizeof(float));
  add<<<blocks, threads>>>(a,b,c);
  hipDeviceSynchronize();
}
CUDA (NVIDIA)
#include <cuda_runtime.h>
__global__ void add(float* a, float* b, float* c) {
  int i = blockIdx.x * blockDim.x + threadIdx.x;
  c[i] = a[i] + b[i];
}
int main() {
  float *a, *b, *c;
  cudaMalloc(&a, N*sizeof(float));
  cudaMalloc(&b, N*sizeof(float));
  add<<<blocks, threads>>>(a,b,c);
  cudaDeviceSynchronize();
}

Kernel body is identical — only the API prefix differs.

07 ROCm tool tour

Six tools cover the full cycle: monitor → query → compile → profile → trace → compute.

toolwhat it doeswhen you use it
amd-smiGPU dashboard: temp, clocks, memory, power, utilization.Every session — is it busy? overheating?
rocminfoLists GPUs: name, CU count, ISA, wavefront, clocks.First thing after install — does ROCm see the GPU?
hipccHIP compiler — auto-detects GPU, emits native code.Every compile: hipcc -O3 -o out in.cpp
rocprofv3Kernel times, HW counters, occupancy metrics.Profiling: rocprofv3 --stats -- ./app
rocBLASBLAS / GEMM — matrix-multiply engine.Frameworks call it for GEMMs; hipfire calls it for CDNA3 prefill GEMMs (§ 18).
MIOpenDL primitives: conv, batchnorm, pooling, RNNs.PyTorch / TF call it (miopenConv*).

08 How your code maps to the chip

The kitchen analogy — how software entities land on the hardware:

kitchen analogy software hardware
recipeKERNEL — the function codeloaded into every CU's instruction cache
all the mealsGRID — all threads launcheddispatched across the whole GPU
a team of cooksBLOCK — 64–1024 threadsruns on ONE CU (shares that CU's LDS)
a squad of 64 in syncWAVEFRONT — 64 threads of a blockruns on ONE SIMD (16 lanes wide: each instruction takes 4 cycles)
one cookTHREAD — one work-itemexecutes on ONE ALU lane
the fridgeMEMORY — global arraysstored in HBM (fetched via L2 → L1)

09 SIMT — the GPU execution model

Single Instruction, Multiple Threads — write code for ONE thread; the GPU runs thousands of copies, grouped into 64-thread wavefronts on CDNA.

fig · one wavefront, 64 threads in lockstep
 kernel pc → ┐
             ▼
 T0  T1  T2  T3  T4  ... T60 T61 T62 T63   ← 64 lanes, all advancing one
 ▏   ▏   ▏   ▏   ▏        ▏   ▏   ▏   ▏      instruction per cycle
 ───────────── 1 wavefront ─────────────
        
  • Write code for one thread. The kernel describes work for a single thread; the hardware spawns thousands of copies — one per work-item.
  • 64 threads = 1 wavefront. The scheduler issues 64 threads together. They share a program counter; a CDNA SIMD is 16 lanes wide, so one wave64 instruction occupies it for 4 cycles.
  • MI300X scale: 304 CUs × 4 SIMDs × 16 lanes = 19,456 ALU lanes executing at once, with many more threads resident per SIMD so a stalled wave can be swapped out.

10 Wavefronts & latency hiding

A wavefront is the GPU's unit of scheduling: 64 threads in lockstep. Keeping many wavefronts in flight hides memory latency — on memory stall, the CU scheduler switches to another wavefront with zero context-switch cost.

interactive · latency hiding · three wavefronts, one stall
WF A
running
WF B
queued
WF C
queued
running on SIMD stalled on HBM queued / done CU: busy

11 Three bottleneck types

Every kernel sits in one of three regimes. The optimization recipe is different for each:

regime limit symptom on roofline fix
MEMORY-BOUND data movement HBM ↔ cache / LDS Low AI · mem pipes near peak · ALUs idle waiting left of ridge Coalesce, stage reuse in LDS, raise AI via fusion, smaller precision
COMPUTE-BOUND ALU / MFMA throughput ALUs or matrix engines near peak · memory mostly idle at the compute roof Use MFMA, reduce precision (FP8 / BF16), increase ILP, fewer redundant ops
OVERHEAD-BOUND host scheduling, launch, tiny arrays GPU mostly idle · few wavefronts active · many tiny launches far below both roofs Larger workgroups, fewer fatter kernels, HIP graphs, batch small launches

12 MI300 — package, XCD, compute unit

From the full board (package + HBM + I/O) down to one CU's internals. Three zoom levels:

fig · MI300X zoom
 [ MI300X package ]
 ┌──────────────────────────────────────────────────────┐
 │  HBM3        XCD       XCD       XCD       XCD  HBM3 │
 │  HBM3        XCD       XCD       XCD       XCD  HBM3 │
 │  HBM3            PCIe 5  +  Infinity Fabric I/O  HBM3│
 │  HBM3                                             HBM3│
 └──────────────────────────────────────────────────────┘
        8 XCDs · 8 HBM3 stacks · 192 GB total HBM3

 [ one XCD ]                          [ one CU ]
 ┌────────────────────┐               ┌─────────────────┐
 │ ▢ ▢ ▢ ▢ ▢ ▢ ▢ ▢   │               │ SIMD0 SIMD1     │
 │ ▢ ▢ ▢ ▢ ▢ ▢ ▢ ▢   │   ────→       │ SIMD2 SIMD3     │
 │ ▢ ▢ ▢ ▢ ▢ ▢ ▢ ▢   │               │ + MFMA  + scalar│
 │ ▢ ▢ ▢ ▢ ▢ ▢       │               │ LDS 64 KB · L1  │
 └────────────────────┘               └─────────────────┘
   ~38 CUs / XCD                       4 SIMDs × 16 ALUs = 64 lanes
   304 active CUs total                + matrix engine + LDS + L1
        

13 MFMA — one instruction, one full matrix tile

The matrix engine computes a 16×16 tile multiply-accumulate in a single instruction — the building block of every GEMM.

interactive · v_mfma_f32_16x16x16_f16 · one instruction, one tile
A · fp16 · 16×16
×
B · fp16 · 16×16
+
C · fp32 · accumulator
=
D · fp32 · result
1 issue · 4096 fp16 FMA per 16×16×16 issue
  • What it is: fused matrix op — one instruction does a full tile MMA.
  • How it works: each CU has its own MFMA unit, pipelined alongside SIMD ALUs.
  • Why it matters: GEMMs dominate transformer prefill and training, and rocBLAS / hipBLASLt route them to MFMA.
  • Variants: 16×16×16 fp16, 32×32×8 bf16, FP8 on CDNA 3, FP6 / FP4 on CDNA 4.

14 CDNA vs RDNA — two architectures

Same parent, different goals. The differences matter because porting CUDA code or tuning across families hits real pitfalls.

CDNA — Instinct
data-center AI & HPC
RDNA — Radeon
consumer / workstation graphics
Wavefront64 threads32 threads
MemoryHBM (HBM3 on MI300X, HBM3E on MI355X)GDDR6, or LPDDR5X on APUs
Cachelarge L2, compute-optimizedgraphics-optimized L1 + GL1
ISA familygfx908, gfx90a, gfx942, gfx950gfx1100, gfx1200, gfx1201
ExamplesMI300X, MI355XRX 9070, Radeon AI PRO R9700, Strix Halo

Same HIP source, different machine code: hipcc reads the ISA family.

15 Why CDNA has no graphics pipeline

Every transistor on a CDNA die goes to compute. The result is a radically different chip from a gaming GPU — even though both are “GPUs.”

transistor budget — what it spends on ( + )
  • More compute units.
  • Bigger matrix engines.
  • Larger caches.
  • More HBM controllers.
what it gives up ( − )
  • No rasterizers.
  • No ray-tracing units.
  • No display outputs.

The result:

304 CUs
MI300X (CDNA 3)
192 GB HBM3
96 CUs
RX 7900 XTX (RDNA 3)
24 GB GDDR6
3× / 8×
more CUs / more memory
with no silicon spent on graphics

16 Memory hierarchy — CDNA edition

Every step from registers to HBM is a big drop in bandwidth and a big rise in latency, and that gap drives every optimization strategy: keep hot data as close to the CU as possible.

levelscopeMI300X (CDNA 3)MI355X (CDNA 4)
VGPR fileper CU512 KiB512 KiB
LDSper CU64 KiB160 KiB
L1 vector cacheper CU32 KiB32 KiB
L2per XCD4 MiB (32 MiB total)4 MiB (32 MiB total)
Infinity Cachedevice256 MiB256 MiB
HBMdevice192 GB HBM3
5.3 TB/s
288 GB HBM3E
8 TB/s

Capacities from AMD's ROCm GPU specification tables; HBM size and peak bandwidth from AMD's Instinct product pages.

If a kernel touches the same byte twice, the second touch should come from registers, LDS or L1, not HBM.

17 Memory optimization recipes

technique aspect rule scope
Coalescingglobal memory access patternMake consecutive threads in a wavefront read consecutive addresses.CU → L2 → HBM
LDS bank conflictsshared-memory access patternPad shared-memory arrays so threads in a wavefront hit different banks.CU → LDS
Cache behaviourL1 / L2 / Infinity Cache reuseIf you reuse data, keep it cached; if you stream once, bypass with glc/slc.CU → L1 → L2 → HBM
Vectorised loadsinstructions-per-byteWhen threads read consecutive floats, use float4 to fetch 16 bytes per instruction.instruction-level
Data layoutrow-major / col-major / tilingLay your arrays out so neighbouring threads naturally land on neighbouring addresses.structure-time

Workflow: profile first (rocprofv3) → identify the bottleneck → apply the matching technique.

18 Where MFMA sits in hipfire 0.4.1

hipfire's tuned kernel families target RDNA3/3.5 and RDNA4 through WMMA. On CDNA the engine runs correctly through a portable wave64 path. It knows gfx908 and gfx940–gfx942 (plus Vega gfx906) as wave64-native targets, and reaches MFMA in two ways:

  • rocBLAS on CDNA3, by default. On gfx940–gfx942, when the rocBLAS library is available and outside graph capture, prefill GEMMs of at least 4 rows (HIPFIRE_ROCBLAS_MIN_BATCH) dequantize the 4-bit weights into a cached FP16 shadow and call rocBLAS, which issues MFMA. Below 4 rows, launch overhead outweighs the compute win and the hand-written wave64 GEMV stays in charge.
  • Hand-written MFMA kernels, opt-in. A set of gfx942 MFMA prefill kernels for HFQ4 weights reads the quantized weights directly, skipping the FP16 shadow (HIPFIRE_GFX942_MFMA_PREFILL=1…4, at least 16 rows). They are not a default.
no CDNA numbers in 0.4.1

The 0.4.1 release matrix measured Strix Halo, the R9700 and the 7900 XTX. There are no 0.4.1 measurements on CDNA, so this page makes no CDNA performance claim.

// crates/rdna-compute/src/dispatch.rs (0.4.1)
fn rocblas_arch_eligible(&self) -> bool { /* … */ self.arch_caps.is_cdna3() }
fn rocblas_min_batch(&self) -> usize { /* HIPFIRE_ROCBLAS_OFF → usize::MAX */ self.flags.rocblas_min_batch.unwrap_or(4) }

19 Summary — what to remember

  • CDNA wave width is 64. Every __shfl_*, occupancy calculation and MFMA builtin assumes 64 lanes; a SIMD is 16 lanes wide, so a wave64 instruction takes 4 cycles. The number flips to 32 on RDNA.
  • MFMA is the CDNA matrix unit. One instruction does a full 16×16 (or 32×32) tile multiply-accumulate; rocBLAS and hipBLASLt route GEMMs to it.
  • HBM is the DRAM tier: 192 GB at 5.3 TB/s on MI300X. MI300X and MI355X add a 256 MiB Infinity Cache; earlier CDNA parts have none.
  • The roofline tells you which optimization to try first. Memory-bound → coalesce / fuse / reuse. Compute-bound → MFMA / lower precision / ILP. Overhead-bound → fewer fatter kernels / HIP graphs.
  • rocprofv3 gives you the counters that matter: VALUBusy, MemUnitBusy, L2CacheHit.
  • The same HIP source compiles for either family, but a CDNA kernel ported to RDNA needs new matrix intrinsics and a wavefront-width audit.
  • In hipfire 0.4.1, CDNA runs the portable wave64 path, with MFMA through rocBLAS for CDNA3 prefill.

20 Sources