An Educational GEMM Ladder for Helios GPUs AMD's Helios GPU, the Instinct MI455X, packs 432 GB of HBM4, 23 TB/s of HBM bandwidth per GPU, and 40 PFLOPs of FP4 compute, according to AMD's product brochure. A new educational blog post builds a ladder of BF16 general matrix multiplication (GEMM) kernels in HipKittens to show kernel developers how Helios's architecture — 256 workgroup processors across eight Accelerator Complex Dies, 320 KB of LDS per WGP, and a 72-GPU rack scale-up domain at 3.6 TB/s — affects kernel design. The ladder is inspired by Simon Boehm's CUDA GEMM worklog and targets large frontier models and long-context agentic workloads. An Educational GEMM Ladder for Helios GPUs an-educational-gemm-ladder-for-helios-gpus AMD Helios will be an important platform for AI. Helios offers 432 GB of HBM4, 23 TB/s of HBM bandwidth per GPU, and 40 PFLOPs of FP4 compute AMD Instinct™ MI455X GPU https://www.amd.com/content/dam/amd/en/documents/products/accelerators/instinct/amd-instinct-mi455x brochure.pdf . These capabilities will be especially valuable for large frontier models and long-context agentic workloads. In this blog post, we highlight several features of the Helios architecture and build an educational ladder of BF16 general matrix multiplication GEMM kernels that progressively takes advantage of them. The ladder is inspired by Simon Boehm’s CUDA GEMM worklog How to Optimize a CUDA Matmul Kernel for cuBLAS-like Performance: a Worklog https://siboehm.com/articles/22/CUDA-MMM and is intended to help kernel developers understand how Helios’s new hardware features affect kernel design. A HipKittens Refresher a-hipkittens-refresher Both the kernel implementations and optimization ladder in this post use HipKittens, so we collect the main references here before diving in. The framework is introduced in HipKittens: Fast and Furious AMD Kernels https://arxiv.org/abs/2511.08083 , and the HipKittens repository https://github.com/HazyResearch/HipKittens contains its source and kernel examples. Helios Feature Overview helios-feature-overview A Helios GPU contains 256 workgroup processors WGPs , organized into eight Accelerator Complex Dies XCDs . Each WGP has 320 KB of local data share LDS and 1024 32-bit registers per wave. The GPU has 432 GB of HBM4 with 23 TB/s of peak bandwidth. The Helios scale-up domain includes 72 GPUs per rack with 3.6 TB/s of bandwidth and a unified virtual-memory abstraction that simplifies intra-node memory access. | Hardware unit | Description | |---|---| | Single Instruction Multiple Data processor SIMD | A group of 32 lanes with its own set of vector general-purpose registers VGPRs . | | Workgroup processor WGP | One of the GPU’s 256 processors. Previously referred to as a compute unit CU on earlier AMD GPU generations. A WGP contains two SIMD pairs, or four SIMDs in total. | | Shader Engine SE | A collection of 16 physically co-located WGPs. | | Accelerator Complex Die XCD | A collection of 32 physically co-located WGPs on a chiplet. | | I/O Die IOD | A base die with four XCDs stacked on top. Each IOD contains 96 MiB of coherent L2 cache. | | GPU | One AMD Instinct™ MI455X GPU consists of two IODs. | Table 1. Physical compute hierarchy of a CDNA™ 5 Helios GPU. | Execution unit | Description | |---|---| | Thread | The smallest unit of execution on the GPU. | | Wave | A collection of 32 threads that executes in lockstep. Earlier AMD GPUs used 64 threads per wave. | | Workgroup | A collection of waves co-scheduled on a WGP. | | Workgroup cluster | A collection of workgroups running concurrently on a Shader Engine. | | Grid | The complete collection of workgroups, or workgroup clusters, launched by one kernel. | Table 2. Logical HIP execution hierarchy on CDNA™ 5. | Memory | Description | |---|---| | VGPR | The SIMD-scoped vector register file: 1024 registers, each with 32 lanes of 32-bit values. | | LDS/L1 | Each WGP has six 64 KB hardware partitions. Up to five 320 KB can be allocated to LDS, with at least one retained for L1. | | L2 | Two coherent 96 MB halves, one per IOD, totaling 192 MB per device. | | High-Bandwidth Memory HBM | Eight 54 GB HBM4 stacks, totaling 432 GB. | Table 3. Physical memory hierarchy of a CDNA™ 5 Helios GPU. The key changes at each level of the memory hierarchy include: - Partitioned LDS. Each WGP has five 64 KB LDS partitions. LDS remains banked, so layouts must avoid bank conflicts. Two 256-byte-per-cycle paths, one per SIMD pair, serve LDS. Concurrent accesses to the same partition can cause partition conflicts, so high-bandwidth kernels must consider both bank placement within a partition and placement across partitions. A single 256-byte-per-cycle path is already sufficient to saturate the matrix core units. - Cache structure, NUMA effects, and memory prefetching. Earlier AMD GPUs used both a per-XCD L2 cache and a global last-level cache LLC . Helios simplifies this hierarchy to a single L2 cache, physically implemented as two coherent 96 MB halves per GPU. The half closer to a given processor provides substantially higher bandwidth than the remote half more than 40 TB/s for near L2 versus approximately 20 TB/s for remote L2 . Across the GPU’s eight XCDs, four XCDs reside in each local L2 NUMA domain. Cache hints let kernel developers manage L2 behavior, including prefetching global memory into L2 from either the device or the host. - Tensor Data Movement TDM for global HBM. TDM provides a DMA-style path between HBM and LDS. It supports scatter-gather access patterns and exposes its descriptor architecture in the ISA. Unlike a hardware-swizzled load, TDM does not rearrange LDS data on the fly, so padding or layout design is still required to avoid bank conflicts. The key changes to the execution model include: - Wave size. Helios uses 32 threads per wave, compared with 64 threads per wave on previous AMD GPUs. On earlier generations, a 64-thread wave executed across 16 physical SIMD lanes, creating less regular lane ownership and memory-access patterns that kernel programmers had to account for when optimizing memory layouts AMD GPUs go brrr https://hazyresearch.stanford.edu/blog/2025-11-09-amd-brr . \ 1\ id2 Helios pairs 32-thread waves with 32 physical SIMD lanes, enabling more regular lane ownership and simplifying memory-layout optimization. - Workgroup-cluster launch and multicast. Helios can guarantee that groups of up to 16 workgroups are co-located across nearby WGPs, enabling data sharing and synchronization across the cluster. Instead of having every workgroup independently request the same data, one load can be multicast to multiple WGPs, increasing effective bandwidth through cache reuse. Now let’s put these features into action. Educational GEMM Ladder educational-gemm-ladder Inspired by Simon Boehm’s GEMM worklog, we present an educational GEMM ladder for Helios GPUs. Figure 1 introduces the MI455X hardware hierarchy and shows how GEMM tiles map onto it: Figure 1: The MI455X hierarchy narrows from the GPU to XCDs, WGPs, SIMDs, waves, and threads top . GEMM maps A and B tiles to WGPs that accumulate C output tiles bottom . For a large GEMM, the output matrix is divided into tiles that can be computed independently. Each workgroup, a collection of waves co-scheduled on a WGP, computes one output tile. Every WGP has its own register file and LDS, as well as circuitry for matrix multiplication, exponentials, and other arithmetic in data types including BF16, FP8, FP6, and FP4. All WGPs can also access the GPU’s shared cache hierarchy and HBM. Figure 2 summarizes the measured performance across the optimization ladder: Even a kernel midway through the ladder outperforms the well-optimized MI355X GEMM kernel, and the final Helios kernels approach twice its performance. These tests were run on early-access GPUs, which continue to receive substantial firmware and software improvements. Each rung computes $C=AB$, where $A \in \mathbb{R}^{M \times K}$, $B \in \mathbb{R}^{K \times N}$, and $C \in \mathbb{R}^{M \times N}$. The inputs and output use BF16 precision. The kernels are written with HipKittens: Fast and Furious AMD Kernels https://github.com/HazyResearch/HipKittens . For each kernel, we report the PFLOP/s attained for $M=N=K=8192$, using 500 warm-up iterations and 100 measured iterations with the L2 cache cleared. The exact benchmarking scripts are available in the HipKittens repository. For each rung, we also show the kernel’s hot loop—that is, its iteration over the GEMM K dimension—captured with AMD Advanced Thread Trace ATT using the profiling tools in the ROCm Systems repository https://github.com/ROCm/rocm-systems . In these visualizations, each row depicts one wave’s instruction execution over time, and a group of rows shows execution on one or more of the WGP’s four SIMDs. Level 0: Naive Baseline gemm naive.cpp https://github.com/HazyResearch/HipKittens/blob/1602364f4f40b5caeec0ccbbaf9ca31f784f1599/kernels/cdna5/gemm/bf16fp32/gfx1250/00 gemm naive.cpp L58-L82 level-0-naive-baseline-gemm-naive-cpp Each workgroup computes a $64 \times 64$ output tile using four waves. The waves are arranged in a $2 \times 2$ grid; each wave computes a $32 \times 32$ region of the output tile and maintains a corresponding register tile for accumulation. The kernel iterates over the K dimension in chunks of 32. During each iteration, all threads cooperatively load $64 \times 32$ tiles of A and B from global memory into LDS. After synchronizing, each wave loads its A and B subtiles from LDS into registers, performs the matrix multiplication, and accumulates the result in its output tile. This baseline uses one LDS buffer for A and B and does not overlap data movement with compute. Every K iteration therefore proceeds serially: load A and B from global memory into LDS, synchronize, load from LDS into registers and compute, synchronize again, and only then begin loading the next K tile. The second synchronization is required because the same LDS buffer is reused in every iteration. As a result, the matrix units are idle during memory movement, and the memory pipeline is underutilized during computation. The figure below shows the resulting serialized schedule: Level 0 APIs level-0-apis | API | Purpose | |---|---| | load A LDS, A global https://github.com/HazyResearch/HipKittens/blob/1602364f4f40b5caeec0ccbbaf9ca31f784f1599/include/cdna5/ops/warp/memory/tile/global to register.cuh L29-L98 | Uses vector lanes to copy a global tile through registers into LDS. | | load A reg, A LDS https://github.com/HazyResearch/HipKittens/blob/1602364f4f40b5caeec0ccbbaf9ca31f784f1599/include/cdna5/ops/warp/memory/tile/shared to register.cuh L845-L914 | Loads one wave’s LDS fragment into registers. | | sync::fence https://github.com/HazyResearch/HipKittens/blob/1602364f4f40b5caeec0ccbbaf9ca31f784f1599/include/cdna5/ops/warp/sync/barrier.cuh L180-L216 | Drains memory traffic before LDS is published or reused. | | sync::sync https://github.com/HazyResearch/HipKittens/blob/1602364f4f40b5caeec0ccbbaf9ca31f784f1599/include/cdna5/ops/warp/sync/barrier.cuh L156-L170 | Waits for every wave at the workgroup barrier. | | mma ABt C, A reg, B reg https://github.com/HazyResearch/HipKittens/blob/1602364f4f40b5caeec0ccbbaf9ca31f784f1599/include/cdna5/ops/warp/register/tile/mma.cuh L280-L308 | Accumulates a BF16 $AB^T$ product into FP32 registers. | | store C global, C acc https://github.com/HazyResearch/HipKittens/blob/1602364f4f40b5caeec0ccbbaf9ca31f784f1599/include/cdna5/ops/warp/memory/tile/global to register.cuh L127-L196 | Converts and writes the FP32 accumulator directly to the global C tile. | Level 0 Pseudocode level-0-pseudocode php for each K tile: load A LDS, A global ; // A: global - staging registers - LDS load B LDS, B global ; // B: global - staging registers - LDS sync::fence ; // Wait for global-to-LDS traffic sync::sync ; // Wait for peer waves to publish LDS load A reg, A LDS ; // A: LDS - registers load B reg, B LDS ; // B: LDS - registers mma ABt C, A reg, B reg ; // Accumulate C += A B^T sync::fence ; // Wait for LDS reads sync::sync ; // Wait before reusing LDS The trace in Figure 4 shows one SIMD with 12 resident wave tracks from different workgroups; the scheduler switches among them automatically to maximize resource utilization: On SIMD0 wave slot 0, early green VALU instructions come from register-mediated A/B fills and address calculations. Four purple WMMA instructions at cycles 1,218–1,620 map to mma ABt . The long yellow intervals are consistent with publish and reuse synchronization, although the color alone does not identify a specific barrier. Level 1: Double-Buffered in LDS gemm double buf.cpp https://github.com/HazyResearch/HipKittens/blob/1602364f4f40b5caeec0ccbbaf9ca31f784f1599/kernels/cdna5/gemm/bf16fp32/gfx1250/01 gemm double buf.cpp L65-L89 level-1-double-buffered-in-lds-gemm-double-buf-cpp - Performance: Less than 1% faster than Level 0 25.3% to 25.4% of the MI355X baseline in Figure 2 . The previous kernel severely underutilizes the 320 KB of LDS available per WGP. At a $64 \times 64$ output tile and BLOCK K=32 , Level 0 uses one 8.5 KB stage—only 2.7% of the budget. Two stages require 17 KB, or 5.3%, so double buffering is a natural next step. This kernel allocates two LDS buffer sets for A and B and turns the K loop into a two-stage software pipeline. Initially, a prologue loads and publishes the first A/B tiles; then, each iteration issues HBM loads into the inactive buffer while WMMA consumes the current one. A workgroup barrier at the end ensures the next buffer is ready to read and the current buffer is safe to overwrite before swapping. Why it helps: Double buffering overlaps memory loads with computation, increasing instruction-level parallelism. The measured performance step stays small here because fills are still register-mediated and each K block drains fully before handoff. Level 2 keeps the same staging; with async direct-to-LDS copies, that is when the benefits of this buffering are realized. The figure below illustrates the double-buffered schedule: Level 1 APIs level-1-apis | API | Purpose | |---|---| | allocate in