Writing Custom Triton Kernels for Memory-Efficient Attention
When scaling transformer training and inference, memory bandwidth is almost always the bottleneck rather than compute floating-point capability. Standard PyTorch implementations of multi-head attention compute intermediate activations into Global Memory (HBM).
For sequence lengths exceeding 8,000 tokens, this leads to quadratic memory growth (O(N^2)) and severe bus traffic between DRAM and SRAM.
The Memory Hierarchy Bottleneck
On modern hardware like the NVIDIA A100 or H100, Global Memory is vast but slow (approx. 2TB/s bandwidth), whereas SRAM (L1/Shared Memory) operates at over 19TB/s but is constrained in capacity (192KB per SM).
Our custom Triton kernel aims to:
- Load block-wise tiles of Query (Q), Key (K), and Value (V) directly into SRAM.
- Compute softmax locally using running max and exponent normalization statistics.
- Stream outputs back to Global Memory without materializing the N × N attention matrix.
1import torch2import triton3import triton.language as tl4 5@triton.jit6def _attn_fwd_kernel(7 Q, K, V, sm_scale, Out,8 stride_qz, stride_qh, stride_qm, stride_qk,9 BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr10):11 start_m = tl.program_id(0)12 off_m = start_m * BLOCK_M + tl.arange(0, BLOCK_M)13 14 # Load Query tile into SRAM registers15 q = tl.load(Q + off_m[:, None] * stride_qm)By leveraging this block-level fusion, memory traffic drops from O(N^2) to O(N), unlocking a 2.5x speedup on sequence lengths larger than 4,096.