Helion on TPU: Towards Hardware Heterogeneous Kernel Authoring Helion, PyTorch's high-level DSL for writing performance-portable ML kernels, partnered with Google to build a TPU backend that compiles Helion kernels to Pallas, enabling PyTorch-friendly TPU kernel authoring. On a flash attention workload, the Helion-generated kernel achieves 838 TFLOPs (~79% MFU of one tensor core) on TPU v7, with autotuning over different code-generation strategies to select the optimal pipelining schema. Featured projects TL;DR Helion https://helionlang.com/ is PyTorch’s high-level DSL for writing performance-portable ML kernels. Partnering with Google, we have built a TPU backend that compiles Helion kernels to Pallas https://docs.jax.dev/en/latest/pallas/index.html , providing a PyTorch-friendly way to author performant TPU kernels. On a flash attention workload, the Helion-generated kernel achieves 838 TFLOPs ~79% MFU of one tensor core on TPU v7. On different input shapes, Helion autotunes over different code-generation strategies to select the optimal pipelining schema, making the most use of TPU’s available VMEM and compute. Introduction TPUs are increasingly important as an ML compute platform to complement GPUs. Google’s latest TPU v7 Ironwood delivers comparable performance to NVIDIA B200 with a potentially lower total cost of ownership TCO , making TPUs an appealing option for large-scale training and inference workloads. However, authoring TPU kernels traditionally requires expertise in Pallas, a low-level DSL that comes with a steep learning curve and code complexity. Helion https://helionlang.com/ bridges this gap. As PyTorch’s portable DSL for ML kernels, Helion lets users write familiar PyTorch-style code and compiles it to optimized TPU code. Paired with performance wins brought by its autotuner, Helion is evolving towards an attractive option for authoring TPU kernels. Specifically, Helion TPU targets three main use cases: Performance-critical use cases where autotuning is required to explore the configuration space Non-Pallas experts hoping to onboard TPU kernel authoring quickly Cross-hardware users who prefer to maintain the same set of kernels across TPU and GPU This article starts with a brief overview of TPU’s hardware features and programming models as compared to GPUs, and then demonstrates how Helion generates performant Pallas code with ideal pipelining characteristics for different input shapes. TPU Primer TPUs are highly specialized accelerators designed and optimized specifically for machine learning workloads. The architecture https://jax-ml.github.io/scaling-book/tpus/ and programming model of TPUs differ significantly from GPUs. The most prominent difference is that a TPU is a sequential machine featuring wide vector registers and compute units. This contrasts with GPUs, which achieve performance via both massively parallel execution CUDA cores and specialized tensor units tensor cores . | TPU Pallas | GPU CUDA | | |---|---|---| Threading | Sequential Few large workers | Parallel SIMT + tensor core Many small workers | Memory Hierarchy | Explicit memory spaces persistent vs scratchpad memory , Async mem copies required for pipelining. | Implicit caches, HW-managed | As a result, TPUs feature a memory hierarchy that kernel authors must deeply understand, so that the kernels they write can orchestrate when and how data is loaded from the off-chip HBM to the fast on-chip VMEM. A performant Pallas kernel would overlap these HBM< VMEM memory transfers with floating point computation happening in the matrix MXU and vector compute units. Despite the architectural differences, current-generation TPUs and GPUs are highly comparable in raw performance. TPU7x and NVIDIA B200 have very similar BF16 compute TFLOPS and HBM bandwidth — the two most important hardware metrics for modern ML workloads. Helion’s Pallas Codegen To extract maximum performance out of a TPU, Helion’s Pallas codegen aims to maximize software pipelining , ensuring that memory transfers and computation overlap as much as possible. This section illustrates Helion’s three-fold strategy for generating pipelined kernels: - Outer loop: pallas-provided pipelined device invocation pallas call / emit pipeline - Inner loop: autotuned between: - pallas-provided pipelined device-side loop emit pipeline - Pre-fetching all values into VMEM, if possible unroll - pallas-provided pipelined device-side loop - Auto-tuned pipeline buffer sizes Example: add As a simple example, consider the following helion kernel for adding two tensors. php @helion.kernel def add x: torch.Tensor, y: torch.Tensor - torch.Tensor: out = torch.empty like x for tile in hl.tile out.size : out tile = x tile + y tile return out The Helion compiler translates this into two functions: a host-side launcher that tiles the input and invokes the device function in a pipelined fashion, and a device function that operates on VMEM-resident tiles: python def helion add x, y, out : out : = x : + y : def add x: torch.Tensor, y: torch.Tensor : BLOCK SIZE 0 =