Skip to content

Commit 3255385

Browse files
Merge pull request #3028 from madeline-underwood/cpup
Cpup reviewed - ready to merge
2 parents f057913 + 0f7dffb commit 3255385

5 files changed

Lines changed: 27 additions & 30 deletions

File tree

content/learning-paths/servers-and-cloud-computing/cpu_hotspot_performix/_index.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
---
22
title: Find Code Hotspots with Arm Performix
3-
4-
draft: true
5-
cascade:
6-
draft: true
3+
description: Learn how to profile and identify CPU hotspots in C++ applications on Arm Neoverse using Arm Performix flame graphs to guide optimization.
74

85
minutes_to_complete: 30
96

10-
who_is_this_for: Software developers and performance engineers who want to identify code hotspots in applications running on Arm Linux systems.
7+
who_is_this_for: This is an introductory topic for software developers and performance engineers who want to identify code hotspots in applications running on Arm Linux systems.
118

129
learning_objectives:
1310
- Run the Code Hotspots recipe in Arm Performix
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
---
2-
title: Background Information
2+
title: Understand flame graphs and profiling tools
33
weight: 2
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
77
---
88

9-
## Overview
9+
## What are flame graphs?
1010

11-
A flame graph is a visualization built from many sampled call stacks that shows where a program spends CPU time. In a performance investigation it is often the first thing to generate when it is not yet clear which parts of the codebase are affecting execution time. Instead of guessing where the bottleneck is, you take a quick sample of real execution and use the resulting graph to identify the hottest code paths that deserve deeper analysis.
11+
A flame graph is a visualization built from many sampled call stacks that shows where a program spends CPU time. In a performance investigation it's often the first thing to generate when it isn't yet clear which parts of the codebase are affecting execution time. Instead of guessing where the bottleneck is, you take a quick sample of real execution and use the resulting graph to identify the hottest code paths that deserve deeper analysis.
1212

13-
## Example Flame Graph
13+
## How to read a flame graph
1414

1515
The flame graph below shows a typical profiling result.
1616

17-
![A flame graph showing function call stacks. The x-axis shows relative sample frequency and the y-axis shows call stack depth, with the widest blocks representing functions that consumed the most CPU time#center](./flame-graph-example.jpg "Example flame graph")
17+
![A flame graph showing function call stacks with the x-axis representing relative sample frequency and the y-axis showing call stack depth. Wider blocks indicate functions that consumed more CPU time during profiling.#center](./flame-graph-example.jpg "Example flame graph")
1818

1919
The x-axis represents the relative number of samples attributed to code paths, ordered alphabetically, not a timeline. A wider block means that function appeared in more samples and therefore consumed more CPU time. The y-axis represents call stack depth. Frames at the bottom are closer to the root of execution, such as a thread entry point, and frames above them are functions called by those below.
2020

2121
Each sample captures a snapshot of the current call stack. Many samples are then aggregated by grouping identical stacks and summing their counts, which is what makes flame graphs compact and readable. A common workflow is to start with the widest blocks, then move upward through the stack to understand which callees dominate that hot path. Reliable stack walking depends on frame pointers being present; they allow the profiler to unwind through nested calls and produce accurate stacks that merge cleanly into stable blocks.
2222

23-
This Learning Path does not cover flame graphs in depth. To learn more, see [Brendan Gregg's flame graph reference](https://www.brendangregg.com/flamegraphs.html).
23+
This Learning Path doesn't cover flame graphs in depth. To learn more, see [Brendan Gregg's flame graph reference](https://www.brendangregg.com/flamegraphs.html).
2424

2525
## Tooling options
2626

27-
On Linux, flame graphs are commonly generated from samples collected with `perf`. perf periodically interrupts the running program and records a stack trace, then the collected stacks are converted into a folded format and rendered as the graph. Sampling frequency is important. If the frequency is too low you may miss short-lived hotspots, and if it is too high you may introduce overhead or skew the results. To make the output informative, compile with debug symbols and preserve frame pointers so stacks resolve to meaningful function names and unwind reliably. A typical build uses `-g` and `-fno-omit-frame-pointer`.
27+
On Linux, flame graphs are commonly generated from samples collected with `perf`. perf periodically interrupts the running program and records a stack trace, then the collected stacks are converted into a folded format and rendered as the graph. Sampling frequency is important. If the frequency is too low you might miss short-lived hotspots, and if it's too high you might introduce overhead or skew the results. To make the output informative, compile with debug symbols and preserve frame pointers so stacks resolve to meaningful function names and unwind reliably. A typical build uses `-g` and `-fno-omit-frame-pointer`.
2828

29-
Arm has built a tool, Arm Performix that simplifies this workflow through the Code Hotspots recipe, making it easier to configure collection, run captures, and explore the resulting call hierarchies without manually stitching together the individual steps. This is the tooling solution you will use in this Learning Path.
29+
Arm has built a tool, Arm Performix that simplifies this workflow through the Code Hotspots recipe, making it easier to configure collection, run captures, and explore the resulting call hierarchies without manually stitching together the individual steps. This is the tooling solution you'll use in this Learning Path.

content/learning-paths/servers-and-cloud-computing/cpu_hotspot_performix/how-to-2.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Setup
2+
title: Build the example application
33
weight: 3
44

55
### FIXED, DO NOT MODIFY
@@ -13,15 +13,15 @@ This Learning Path uses a hands-on worked example to make sampling-based profili
1313
A fractal is a pattern that shows detail at many scales, often with self-similar structure. Fractals are usually generated by repeatedly applying a simple mathematical rule. In the Mandelbrot set, each pixel corresponds to a complex number, which is iterated through a basic recurrence. How quickly the value “escapes” (or whether it stays bounded) determines the pixel’s color and produces the familiar Mandelbrot image.
1414

1515
You don't need to understand the Mandelbrot algorithm in detail to follow this Learning Path — it's used here as a convenient, compute-heavy workload for profiling. To learn more, see the [Mandelbrot set article on Wikipedia](https://en.wikipedia.org/wiki/Mandelbrot_set).
16-
16+
1717

1818
## Connect to Target
1919

20-
See the [Arm Performix installation guide](https://learn.arm.com/install-guides/atp) if this is your first time setting up Arm Performix. In this Learning Path you will connect to an AWS Graviton3 metal instance (`m7g.metal`) with 64 Neoverse V1 cores, your remote target server. From the host machine, test the connection to the remote server by navigating to **Targets** > **Test Connection**. You should see the successful connection screen below.
20+
If this is your first time setting up Arm Performix, follow the [Arm Performix installation guide](/install-guides/atp/). In this Learning Path you'll connect to an AWS Graviton3 metal instance (`m7g.metal`) with 64 Neoverse V1 cores, your remote target server. From the host machine, test the connection to the remote server by navigating to **Targets** > **Test Connection**. You should see the successful connection screen below.
2121

2222
![The Arm Performix Targets panel showing a successful connection test result for a remote Arm server#center](./successful-connection.jpg "Successful connection to remote target")
2323

24-
## Build Application on Remote Server
24+
## Build application on remote server
2525

2626
Connect to the remote server using SSH or Visual Studio Code. Install git and the C++ compiler. On dnf-based systems such as Amazon Linux 2023 or RHEL, run:
2727

content/learning-paths/servers-and-cloud-computing/cpu_hotspot_performix/how-to-3.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
---
2-
title: Assess Baseline Performance
2+
title: Profile baseline performance
33
weight: 4
44

55
### FIXED, DO NOT MODIFY
66
layout: learningpathall
77
---
88

9-
## Run Code Hotspots Recipe
9+
## Run Code Hotspots recipe
1010

1111
As shown in the `src/main_single_thread.cpp` file below, the program generates a 1920×1080 bitmap image of the fractal. To identify performance bottlenecks, run the Code Hotspots recipe in Arm Performix (APX). APX uses sampling to estimate where the CPU spends most of its time, allowing it to highlight the hottest functions—especially useful in larger applications where it isn't obvious ahead of time which functions will dominate runtime.
1212

@@ -40,9 +40,9 @@ make clean
4040
make single_thread DEBUG=1
4141
```
4242

43-
Open APX from the host machine. Select the **Code Hotspot** recipe. If this is the first time running the recipe on this target machine you may need to select the install tools button.
43+
Open APX from the host machine. Select the **Code Hotspot** recipe. If this is the first time running the recipe on this target machine you might need to select the install tools button.
4444

45-
![The Arm Performix recipe selection screen with the Code Hotspots recipe highlighted#center](./install-tools.jpg "Selecting the Code Hotspots recipe")
45+
![The Arm Performix recipe selection screen with the Code Hotspots recipe highlighted and the install tools button visible for first-time setup#center](./install-tools.jpg "Selecting the Code Hotspots recipe")
4646

4747
Configure the recipe to launch a new process. APX will automatically start collecting metrics when the program starts and stop when the program exits.
4848

@@ -52,7 +52,7 @@ Use the default sampling rate of **Normal**. If your application is short-runnin
5252

5353
![The Arm Performix Code Hotspots recipe configuration screen showing launch settings, binary path, and sampling rate fields#center](./code-hotspots-config.jpg "Code Hotspots recipe configuration")
5454

55-
## Analyse Results
55+
## Analyze results
5656

5757
A flame graph is generated once the run completes. The default colour mode labels the hottest functions—those using CPU most frequently—in the darkest shade. In this example, the `__complex_abs__` function is present in approximately 65% of samples, and it calls the `__hypot` symbol in `libm.so`.
5858

@@ -62,7 +62,7 @@ To investigate further, you can map source code lines to the functions in the fl
6262

6363
![The Arm Performix flame graph view showing source code annotations mapped to the selected hot function#center](./view-with-source-code.jpg "Flame graph with source code view")
6464

65-
Finally, check your `images` directory for the generated bitmap fractal `green.bmp`
65+
Finally, check your `images` directory for the generated bitmap fractal `green.bmp`. This confirms the application ran successfully and produced the expected Mandelbrot set visualization.
6666

67-
![A rendered Mandelbrot set fractal in green, generated from the single-threaded build at maximum iterations#center](./plot-1-thread-max-iterations.jpg "Mandelbrot fractal output from single-threaded build")
67+
![A rendered Mandelbrot set fractal in green showing the characteristic self-similar structure at maximum iterations. This is the baseline output you'll use for performance comparison.#center](./plot-1-thread-max-iterations.jpg "Mandelbrot fractal output from single-threaded build")
6868

content/learning-paths/servers-and-cloud-computing/cpu_hotspot_performix/how-to-4.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Optimize
2+
title: Optimize application performance
33
weight: 5
44

55
### FIXED, DO NOT MODIFY
@@ -22,7 +22,7 @@ Looking at the `Mandelbrot::getIterations` function, there are two clear optimiz
2222
}
2323
```
2424
25-
### Optimization 1 - Limiting loop boundary
25+
### Optimization 1: Limiting loop boundary
2626
2727
The iteration count is bounded by `MAX_ITERATIONS`, defined as 1024, a `static const` integer in the `Mandelbrot.h` header. Halving this to 512 reduces the maximum work per pixel but you will need to verify that the change in image quality is acceptable.
2828
@@ -44,9 +44,9 @@ Select the refresh icon in the top right to rerun the recipe, then switch to com
4444

4545
There is negligible difference in perceived image quality when halving `MAX_ITERATIONS`.
4646

47-
![Side-by-side comparison of Mandelbrot fractal output at MAX_ITERATIONS 1024 and 512, showing no visible quality difference#center](./comparison.jpg "Image quality comparison: 1024 vs 512 iterations")
47+
![Side-by-side comparison of Mandelbrot fractal output at MAX_ITERATIONS 1024 (left) and 512 (right). Both images show the same level of detail and structure, confirming that the reduced iteration count doesn't compromise visual quality.#center](./comparison.jpg "Image quality comparison: 1024 vs 512 iterations")
4848

49-
### Optimization 2 - Parallelising the hot function
49+
### Optimization 2: Parallelizing the hot function
5050

5151
The loop in `Mandelbrot::getIterations` has no loop-carried dependencies — each iteration's result is independent of any other. This means you can parallelize the hot function across multiple threads if your CPU has multiple cores.
5252

@@ -80,9 +80,9 @@ The proportion of samples has not changed significantly overall, but with 64 thr
8080
The total run duration shown in APX includes tooling setup and data analysis time, not just application execution time. To measure only the application, use the `time` command: the application now runs in approximately 1 second — close to a 100x improvement over the original single-threaded baseline.
8181
{{% /notice %}}
8282

83-
### (Optional Challenge) Additional optimizations
83+
### (Optional challenge) Additional optimizations
8484

85-
The `Makefile` uses the `-O0` flag when the `DEBUG=1` argument is passed in. This disables all compiler optimizations. Try experimenting with higher optimization levels, different loop boundary sizes, and thread counts. See the Learning Path [Get started with compiler optimization flags](/learning-paths/servers-and-cloud-computing/cplusplus_compilers_flags/) for guidance. You may also want to explore vectorized math libraries that could replace the `libm` hypotenuse function, such as the [Arm Performance Libraries vector math functions](https://developer.arm.com/documentation/101004/2601/Arm-Performance-Libraries-Math-Functions/Arm-Performance-Libraries-Vector-Math-Functions--Accuracy-Table).
85+
The `Makefile` uses the `-O0` flag when the `DEBUG=1` argument is passed in. This disables all compiler optimizations. Try experimenting with higher optimization levels, different loop boundary sizes, and thread counts. See the Learning Path [Get started with compiler optimization flags](/learning-paths/servers-and-cloud-computing/cplusplus_compilers_flags/) for guidance. You might also want to explore vectorized math libraries that could replace the `libm` hypotenuse function, such as the [Arm Performance Libraries vector math functions](https://developer.arm.com/documentation/101004/2601/Arm-Performance-Libraries-Math-Functions/Arm-Performance-Libraries-Vector-Math-Functions--Accuracy-Table).
8686

8787

8888
## Summary

0 commit comments

Comments
 (0)