Introduction
Welcome to the first lesson of our module on performance analysis. In the previous module, we built a toolkit of fundamental parallel primitives—reduction, scan, and finally, gather and scatter—learning how to structure computations on the GPU. Now, we shift our focus from correctness and structure to a question that is central to your goal of mastering high-performance computing: how fast is my code, and why?
It's a common and often counterintuitive experience for developers new to GPUs to write a kernel, run it on a massively powerful chip, and find that it achieves only a tiny fraction of the hardware's theoretical peak performance. The expensive silicon sits mostly idle, waiting. The natural question is, waiting for what?
This lesson introduces the foundational concept for answering that question: arithmetic intensity. You will learn how to define this crucial metric and use it to classify a kernel as either compute-bound (limited by the speed of its arithmetic units) or memory-bound (limited by the speed of its memory system). This classification is the first and most important step in any performance optimization effort, as it tells you where to focus your attention.
The Two Ceilings: Compute and Memory
Every program you run on a processor, whether a CPU or a GPU, is ultimately constrained by one of two physical limits: how fast it can perform calculations, or how fast it can feed the data required for those calculations. This is the central tension in high-performance computing.
The article "Mastering CUDA and High-Performance Computing, Part IV" eloquently describes this reality. I highly recommend reading the first two sections to set the stage. The author captures the essence of GPU performance engineering, which often has less to do with the arithmetic itself and more to do with the physics of moving data.
Mastering CUDA and High-Performance Computing, Part IV
This article from The Software Frontier provides an excellent, deeply technical narrative about why memory performance is often the real bottleneck in modern GPU programming.
Please read from the beginning of the article through to the end of the section on High Bandwidth Memory. Focus on the core argument presented in these sections: the central problem, the discussion of memory latency's cost, and the introduction of arithmetic intensity as the key to bridging the performance gap.
As the article highlights, there's a vast chasm between the processor's computational throughput (measured in FLOPS, or Floating-Point Operations Per Second) and its memory bandwidth (measured in Bytes per second). To understand which of these is limiting your kernel, we need a way to characterize the kernel's own demand for computation versus its demand for data.
Defining Arithmetic Intensity
We can formalize this relationship using a simple but powerful model. To start, let's watch a short video that introduces the core concepts from first principles.
A very short intro to the Roofline model
This video, "A very short intro to the Roofline model," provides a clear and concise explanation of the simplified hardware model and the definition of computational intensity.
Watch the segment from Hardware and Software View, where the presenter simplifies the hardware into compute and memory units, and the software into work (FLOPs) and data volume (Bytes). Then, continue through the next section, Performance Limits, which explains the two possible performance bottlenecks.
As explained in the video, we can characterize any computational task by two numbers:
- The total number of floating-point operations it performs (Work, or FLOPs).
- The total volume of data it moves to and from main memory to perform those operations (Bytes).
From these, we define Arithmetic Intensity (AI), often denoted by :
The unit of arithmetic intensity is FLOPs/Byte. It tells you, on average, how many calculations your algorithm performs for every single byte it fetches from or stores to global memory. A high AI means the algorithm does a lot of computation on its data, while a low AI means the algorithm is mostly just moving data around.
The Roofline Model: Visualizing Performance Limits
The arithmetic intensity of your kernel, combined with the hardware's peak performance and memory bandwidth, determines its theoretical maximum performance. This relationship is captured in the Roofline Model.
The model states that a kernel's attainable performance (in FLOPs/second) is limited by the minimum of two values: the machine's peak compute throughput and the performance achievable given the machine's memory bandwidth.
Where:
- is the theoretical peak performance of the GPU (e.g., in TFLOPS/s). This is a fixed hardware characteristic.
- is the arithmetic intensity of your kernel (in FLOPs/Byte).
- (beta) is the peak memory bandwidth of the GPU (e.g., in GB/s or TB/s). This is also a fixed hardware characteristic.
This simple equation gives us a powerful classification tool. We can visualize it as a plot with Arithmetic Intensity on the x-axis and Performance (GFLOPS/s) on the y-axis.

The plot shows two distinct regions:
- Memory-Bound Region (Slanted Line): When the arithmetic intensity is low, the performance is limited by . The kernel is "starved for data," and its speed is dictated by how fast the memory system can deliver bytes. We call such kernels memory-bound.
- Compute-Bound Region (Flat Line): When the arithmetic intensity is high, the performance hits a ceiling at . The memory system can supply data fast enough to keep the arithmetic units fully busy. We call such kernels compute-bound.
The "knee" of the roofline plot, where the two lines intersect, is known as the ridge point. This represents the minimum arithmetic intensity a kernel must have to be able to saturate the machine's compute units.
Calculating Arithmetic Intensity in Practice
Let's apply this by calculating the AI for two familiar operations: vector addition and matrix multiplication. The article from Vrushank Desai provides a very clear, step-by-step calculation.
Part II - CUDA Kernel Optimization Tips - Vrushank Desai
This blog post offers a practical guide to roofline analysis, including explicit calculations for common kernels.
First, read the introductory section Reasoning about performance to see the core roofline equation presented. Then, carefully study the two examples provided: Vector Addition and Matrix Multiplication.
Let's summarize the findings from the reading.
Example 1: Vector Addition (C = A + B)
This is the quintessential memory-bound operation. For each element of the output vector C, a kernel must:
- Read one element from vector
A. - Read one element from vector
B. - Perform one addition operation.
- Write one element to vector
C.
Assuming 32-bit floats (4 bytes each):
- FLOPs per element: 1
- Bytes per element: 4 (read A) + 4 (read B) + 4 (write C) = 12 bytes
- Arithmetic Intensity (I): FLOPs/Byte.
This is an extremely low AI. On virtually any modern GPU, this kernel will be heavily memory-bound. Optimizing the addition instruction itself is pointless; performance is entirely determined by memory bandwidth.
Example 2: Naive Matrix Multiplication (C = A * B)
Consider multiplying two matrices.
- Work (FLOPs): To compute each of the elements in the output matrix C, we perform a dot product of an N-element row and an N-element column. This requires multiplications and additions, for a total of FLOPs. The total work is thus FLOPs.
- Data (Bytes): In a naive implementation where we load all data from global memory, we must read matrix A ( elements) and matrix B ( elements), and write matrix C ( elements). Total elements moved: . With 4-byte floats, this is bytes.
The arithmetic intensity is:
This is a fascinating result. Unlike vector addition, the AI of matrix multiplication grows linearly with the problem size, N.
- For a small matrix (e.g., N=32), FLOPs/Byte. This might still be memory-bound.
- For a large matrix (e.g., N=1024), FLOPs/Byte. This is a very high AI and is almost certainly compute-bound.
This explains why optimizing GEMM (General Matrix Multiplication) is so important and effective in deep learning. Large matrix multiplies have enough arithmetic intensity to keep the GPU's powerful compute units fed, making them a prime target for compute-focused optimizations—a topic we will dedicate an entire future module to.

Conclusion
In this lesson, you have learned the fundamental concept of arithmetic intensity and its role in performance analysis. This framework allows you to move beyond guesswork and begin scientifically diagnosing why a kernel performs the way it does.
Key Takeaways:
- A kernel's performance is limited by either its computational requirements or its memory access requirements.
- Arithmetic Intensity (AI) is the ratio of FLOPs performed to bytes transferred from main memory (). It is the defining characteristic of an algorithm's performance profile.
- The Roofline Model provides a visual framework for understanding these limits, showing that attainable performance is the minimum of the hardware's peak compute rate and the bandwidth-limited rate ().
- Kernels with low AI (e.g., vector addition) are memory-bound. Their performance is dictated by memory bandwidth.
- Kernels with high AI (e.g., large matrix multiplication) are compute-bound. Their performance is dictated by the raw compute power of the processor.
Knowing whether your kernel is memory-bound or compute-bound is the first step in optimization. If you are memory-bound, you should focus on reducing memory traffic (e.g., through better data reuse with shared memory). If you are compute-bound, you focus on increasing instruction throughput.
In our next lesson, we will make this theory practical. You will learn how to use NVIDIA Nsight Compute, a professional profiling tool, to measure the actual performance and memory traffic of a kernel, calculate its achieved arithmetic intensity, and begin constructing your first roofline plot to visualize its bottlenecks.