Introduction
In our last lesson, you successfully constructed a high-performance GEMM kernel using wmma intrinsics, mastering the critical data pipeline from global memory to shared memory and into registers. This process highlighted a core principle of GPU programming: performance is often dictated not by raw compute speed, but by the efficiency of data movement. We optimized an algorithm, GEMM, that is inherently well-behaved when tiled correctly.
Now, we will turn our attention to a foundational algorithm in modern AI that is, in its naive form, fundamentally ill-behaved from a memory perspective: self-attention. This lesson will dissect the standard implementation of self-attention to reveal precisely why it creates a severe performance bottleneck. Your goal is to explain the quadratic memory complexity that arises, which in turn motivates the need for advanced, I/O-aware algorithms like FlashAttention. This analysis will draw directly on your understanding of the GPU memory hierarchy and the performance costs of inefficient memory access.
The Computational Steps of Standard Self-Attention
At its core, the self-attention mechanism allows a model to weigh the importance of different tokens in a sequence when producing a representation for each token. The canonical formula for scaled dot-product attention is:
Where (Query), (Key), and (Value) are matrices derived from the input sequence, and is the dimension of the key vectors.
For a sequence of length , the matrices , , and each have a shape of . A naive implementation of this formula on a GPU follows a straightforward, multi-kernel or multi-step process:
- Compute Scores: Calculate the dot product between every query and every key to get the attention scores. This is a matrix multiplication: . The resulting score matrix has dimensions .
- Apply Softmax: Apply a softmax function row-wise to the score matrix to get the attention probabilities, . This matrix also has dimensions .
- Compute Output: Multiply the probability matrix by the value matrix to get the final output. This is another matrix multiplication: .
Each of these steps typically involves a separate kernel launch, leading to multiple trips to and from the GPU's main memory, which, as you know, is a high-latency operation.
To see this in action, watch the following short animation, which visualizes this sequence of operations. Pay close attention to how intermediate matrices ( and , which is our ) are written to and read from High Bandwidth Memory (HBM).
How FlashAttention Accelerates Generative AI Revolution
This video from the Jia-Bin Huang channel provides a clear, animated walkthrough of the standard attention mechanism.
Please watch the segment from the beginning of the self-attention explanation to the end. Notice the repeated "save the result ... back to HBM" and "load the Matrix ... from HBM" steps. This illustrates the memory traffic we are about to analyze.
The diagram below offers a static view of this same process, highlighting the data flow between the compute units and the slow, off-chip HBM.

The Quadratic Memory Bottleneck
The fundamental problem with the standard attention mechanism lies in the materialization of the attention matrix. Let's analyze why this is so detrimental.
For a sequence of length and a model with attention heads, the attention probability matrix has a shape of (h, n, n). The memory required to store this single tensor is proportional to . This is the source of the quadratic memory complexity.
To appreciate the scale of this problem, consider the numbers. In early models, a sequence length of was common. For a 12-head model using 16-bit floats (2 bytes), the memory for the attention matrix is:
This is manageable. However, modern Large Language Models (LLMs) operate on much longer contexts. If we increase the sequence length to :
And at :
As you can see, the memory consumption explodes. For a sequence length of 32k, storing this single intermediate matrix can consume the entire memory of a consumer GPU and a significant portion of a high-end datacenter GPU like an A100. This is before accounting for model weights, gradients, optimizer states, and other activations.
The following resource provides a detailed analysis of this issue, connecting it to a typical PyTorch implementation.
Reimplementing FlashAttention for performance and giggles
This blog post by Amine Dirhoussi gives a fantastic, code-centric breakdown of the problem.
Read the section from the first code block to the end of the paragraph. The author annotates a standard PyTorch implementation to show exactly where the (B, N_h, S, S) tensor is created and written to HBM, explicitly identifying the quadratic memory complexity as the critical bottleneck.
The graph below visually contrasts the linear, , complexity of a Recurrent Neural Network (RNN) with the quadratic, , complexity of self-attention. While attention offers superior modeling capabilities by allowing direct interaction between any two tokens, it comes at a steep computational and memory cost.

The I/O Problem: More Than Just Memory Size
For a systems programmer like yourself, it's clear that the issue isn't just the size of the memory allocation, but the cost of accessing it. The matrix is too large to fit in the fast on-chip SRAM. Consequently, it must be written to and read from the much slower global memory (HBM).
This creates a severe I/O bottleneck. The standard algorithm is not I/O-aware; it is memory-bound. The GPU's powerful compute units spend most of their time idle, waiting for data to be shuttled back and forth from HBM.
Tri Dao, the creator of FlashAttention, explains this exact issue. He highlights that the quadratic scaling of reads and writes to HBM is the primary obstacle to processing long sequences efficiently.
FlashAttention - Tri Dao | Stanford MLSys #67
In this seminar, Tri Dao provides the core motivation for his work on FlashAttention.
First, watch the segment that details the quadratic scaling of reads and writes to HBM. Then, watch the following section on the GPU memory hierarchy. This will be a review of concepts you already know, but it perfectly frames the hardware asymmetry that FlashAttention is designed to exploit: the massive difference in size and speed between HBM and SRAM.
The arithmetic intensity of standard attention is very low. The number of memory operations scales as , while the number of floating-point operations scales as . For long sequences where , the kernel spends far more time waiting on memory than doing useful computation, making it a classic memory-bound problem.
Conclusion
In this lesson, we have pinpointed the fundamental performance limitations of the standard self-attention algorithm. By breaking down its computational steps, we've identified the materialization of the attention matrix as the primary culprit.
Key Takeaways:
- Quadratic Memory Complexity: Standard attention requires storing an intermediate matrix, leading to memory requirements that scale with . This makes processing long sequences prohibitively expensive in terms of memory capacity.
- I/O Bottleneck: The large attention matrix does not fit in fast on-chip SRAM and must be repeatedly written to and read from slower global memory (HBM). This makes the algorithm memory-bound, leaving the GPU's compute units underutilized.
- The Attention Bottleneck: The combination of quadratic memory and I/O scaling creates the "attention bottleneck," which is the primary factor limiting the context length of Transformer models.
You now have a firm grasp of the problem that FlashAttention was designed to solve. It's not about approximating attention; it's about reformulating the exact same computation to be I/O-aware, avoiding the costly round-trips to HBM.
In our next lesson, we will explore the elegant solution. You will learn about the two core techniques FlashAttention employs—tiling and online softmax—to compute exact attention without ever materializing the full matrix in global memory, thereby breaking the quadratic memory barrier.