Profiling in PyTorch (Part 3): Attention is all you profile Hugging Face released the third part of its 'Profiling in PyTorch' series, focusing on profiling attention mechanisms in transformer models. The post demonstrates how to use PyTorch's profiler to analyze naive attention implementations and optimize them using techniques like in-place operations and scaled dot-product attention. Updated • 161 • 2 Profiling in PyTorch Part 3 : Attention is all you profile Update on GitHub https://github.com/huggingface/blog/blob/main/torch-attention-profile.md This is the third post of Profiling in PyTorch, a series where we slowly build the skill of reading profiler traces and use it to drive optimization: The series "Profiling in PyTorch" is meant to make you comfortable reading profiler traces and tables. In Part 1 https://huggingface.co/blog/torch-profiler we profiled basic math operations like addition and multiplication. We saw how the profiler table uncovers hotspots, and how the profiler trace shows the order in which an algorithm runs over time. In Part 2 https://huggingface.co/blog/torch-mlp-fusion we wrapped that addition and multiplication into a torch linear layer. We then stacked several linear layers on top of each other a multilayer perceptron and profiled that. Along the way we also profiled fused and hand-tuned kernels. From the perspective of the Transformer architecture, the next logical step for us to profile is yet another fundamental algorithm, attention. While being infamous for its quadratic-time complexity, many clever tricks exist to mitigate that issue and make it fast. Our goal here is not to cover every trick in detail. Instead, we want to see how each one looks different under the profiler. The scripts for this blog post live here: , 04 a naive attention.py , 04 b inplace ops attention.py , and 04 c sdpa attention.py . Like before, it helps to open them in a separate tab and walk through the code as you read. We use an 04 d kernels attention.py NVIDIA A100-SXM4-80GB GPU to run the scripts. It is really easy to set up a GPU on the Hugging Face infrastructure and experiment with the scripts using Dev Mode with Spaces . One could also run the scripts with the Hugging Face Jobs pipeline . Naive attention Attention works with Queries q , Keys k , and Values v . The interaction between them can be written as a short sequence of steps: - Build the attention scores scores : matmul q, k.T - Scale the scores: scores scale - Apply a causal mask to the scores: scores.masked fill mask, "-inf" - Normalize the scores with softmax to get the attention weights attn : softmax scores - Reweight the values with those weights: matmul attn, v So attention is really a collection of primitive operations. Some of them we already know the matmuls , and the rest are easy to spot. Let's write a naive attention module in PyTorch and profile it. python class NaiveCausalAttention nn.Module : def init self, head dim : super . init self.scale = 1.0 / math.sqrt head dim def forward self, q, k, v, mask : scores = torch.matmul q, k.transpose -2, -1 scores = scores self.scale scores = scores.masked fill mask, float "-inf" attn = torch.softmax scores, dim=-1 out = torch.matmul attn, v return out Before opening the trace, let's do our usual exercise and guess what we should see. Tracing the forward of this module, we expect: - a matmul kernel q . k.T - a mul kernel the scaling - an operation for the masking - a softmax kernel - a matmul kernel atten . v uv run 04 a naive attention.py uvx trace-util -f traces/ -b