You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
title: Set up the target environment and compile the application
3
3
weight: 2
4
4
5
5
### FIXED, DO NOT MODIFY
6
6
layout: learningpathall
7
7
---
8
8
9
-
This Learning Path uses Arm Performix CPU Mircoarchitecture and Instruction Mix recipes to analyze performance in a sample application.
9
+
To analyze performance bottlenecks, you need an environment and a sample application to profile. In this section, you configure an Arm Performix connection and build a Mandelbrot set generator.
10
+
11
+
A Mandelbrot set generator is a classic computer science application used to test computational performance. It calculates a famous mathematical fractal by performing intense, repeated mathematical operations (often floating-point) for every pixel in a large image. Because the math for each pixel is independent of the others, it is a highly parallelizable workload that is perfect for demonstrating CPU optimizations like vectorization and loop unrolling.
10
12
11
13
## Before you begin
12
14
13
-
Use the Performix [installation guide](/install-guides/atp/) to install the tool if this is your first run. From the host machine, open the **Targets** tab, set up an SSH connection to the target that runs the workload, and test the connection. In this Learning Path's examples, I'll connect to an Arm Neoverse V1 workstation.
15
+
Make sure Arm Performix installed on your host machine. The host machine is your local computer where the Arm Performix GUI runs, and it can be a Windows, macOS, or Linux machine. The target machine is the Linux server where your application is compiled and where the application runs.
16
+
17
+
If you do not have Arm Performix installed, see the [Arm Performix install guide](/install-guides/atp/).
18
+
19
+
From the host machine, open the Arm Performix application and navigate to the **Targets** tab. Set up an SSH connection to the target that runs the workload, and test the connection. For the examples in this guide, you connect to an Arm Neoverse-based server.
20
+
21
+
The Arm Performix collection agent requires Python and `binutils` to run on the target machine.
22
+
23
+
Connect to your target machine using SSH and install these required OS packages.
24
+
25
+
For Ubuntu and other Debian-based distributions, run the following command:
14
26
15
-
Install required OS packages on the target. For Debian-based distributions, run:
## Build the sample application on the target machine
21
32
22
-
Connect to your target machine and download the sample application for this Learning Path, a Mandelbrot set generator.
23
-
The code is available under the [Arm Education License](https://github.com/arm-university/Mandelbrot-Example?tab=License-1-ov-file). Create a directory where you want to store and build the example, then run:
33
+
Download the sample application, which is a Mandelbrot set generator provided under the [Arm Education License](https://github.com/arm-university/Mandelbrot-Example?tab=License-1-ov-file). Create a directory to store and build the example, then run the following commands:
title: Find Bottlenecks with CPU Microarchitecture
2
+
title: Identify application bottlenecks with the CPU Microarchitecture recipe
3
3
weight: 3
4
4
5
5
### FIXED, DO NOT MODIFY
6
6
layout: learningpathall
7
7
---
8
8
9
-
## Run CPU Microarchitecture analysis
9
+
## Run the CPU Microarchitecture recipe
10
10
11
-
As shown in the `main.cpp` listing below, the program generates a 1920×1080 bitmap image of the fractal. To identify performance bottlenecks, run the CPU Microarchitecture recipe in Arm Performix (APX). APX uses microarchitectural sampling to show which instruction pipeline stages dominate program latency, then highlights ways to improve those bottlenecks.
11
+
To identify performance bottlenecks, run the CPU Microarchitecture recipe in Arm Performix. Arm Performix uses microarchitectural sampling to show which instruction pipeline stages dominate program latency, and then highlights ways to improve those bottlenecks.
12
12
13
-
14
-
{{% notice Specify the example output file %}}
15
-
Replace the first string argument in `myplot.draw()` with the absolute path to your image folder, then rebuild the application. Otherwise, the image is written to `/tmp/atperf/tools/atperf-agent`, which is periodically deleted.
16
-
{{% /notice %}}
13
+
Start by reviewing the code in `main.cpp`, the program generates a 1920×1080 bitmap image of the fractal.
On your host machine, open Arm Performix and select the **CPU Microarchitecture** recipe.
33
+
When Arm Performix launches the executable on the target machine, it does so from a temporary agent directory, `/tmp/atperf/tools/atperf-agent`. If your code uses a relative path to save the image, the image is written to that temporary folder and might be deleted.
34
+
35
+
To prevent this, edit the `myplot.draw()` line in `main.cpp` to use the absolute path to your project's image folder (for example, `/home/ubuntu/Mandelbrot-Example/images/Green-Parallel-512.bmp`), and then rebuild the application.
36
+
37
+
In the Arm Performix application on your host machine, select the **CPU Microarchitecture** recipe.
34
38
35
-

39
+

36
40
37
-
Select the target you configured in the setup phase. If this is your first run on this target, you likely need to select **Install Tools** to copy collection tools to the target. Next, select the **Workload type**. You can sample the whole system or attach to an existing process, but in this exercise you launch a new process.
41
+
Select the target you configured in the setup section. If this is your first run on this target, you might need to select **Install Tools** to copy the collection tools to the target. After the tools are installed, you see the target is now ready.
38
42
39
-
{{% notice Common Gotcha%}}
43
+
Next, select the **Workload type** and select **Launch a new process**.
44
+
45
+
Enter the absolute path to your executable in the **Workload** field. For example, `/home/ubuntu/Mandelbrot-Example/builds/mandelbrot-parallel`. Make sure to add the number of threads argument.
46
+
47
+
{{% notice Note %}}
40
48
Use the full path to your executable because the **Workload** field does not currently support shell-style path expansion.
41
49
{{% /notice %}}
42
50
43
-
You can set a time limit for the workload and customize metrics if you already know what to investigate.
51
+
Before starting the analysis, you can customize the configuration. For instance, you can set a time limit for the workload or choose specific metrics to investigate. You can also adjust the sampling rate (High, Normal, or Low) to balance collection overhead against sampling granularity. Because this Mandelbrot example is a native C++ application, you can ignore the **Collect managed code stacks** toggle, which is used for Java or .NET workloads.
44
52
45
-
The **Collect managed code stacks** toggle matters for Java/JVM or .NET workloads.
53
+
When your configuration is ready, select **Run Recipe** to launch the workload and collect the performance data.
46
54
47
-
You can also select High, Normal, or Low sampling rates to trade off collection overhead and sampling granularity.
55
+
## View the run results
48
56
49
-
Select **Run Recipe** to launch the workload and collect performance data.
57
+
Arm Performix generates a high-level instruction pipeline view, highlighting where the most time is spent.
Performix generates a high-level instruction pipeline view, highlighting where most time is spent.
61
+
In this breakdown, Backend Stalls dominate the samples. Within that category, work is split between Load Operations and integer and floating-point operations.
62
+
There is no measured SIMD activity, even though this workload is highly parallelizable.
54
63
55
-

64
+
The **Insights** panel highlights ALU contention as a likely improvement opportunity:
56
65
57
-
In this breakdown, Backend Stalls dominate samples. Within that category, work is split between Load Operations and integer and floating-point operations.
58
-
There is no measured SIMD activity, even though this workload is highly parallelizable.
66
+

67
+
68
+
To inspect executed instruction types in more detail, use the Instruction Mix recipe in the next step.
59
69
60
-
The **Insights** panel highlights ALU contention as a likely improvement opportunity.
70
+
## What you've accomplished and what's next
61
71
62
-

72
+
In this section:
73
+
- You ran the CPU Microarchitecture recipe on the Mandelbrot application.
74
+
- You identified that the application spends most of its time in Backend Stalls without using SIMD operations.
63
75
64
-
To inspect executed instruction types in more detail, use the Instruction Mix recipe in the next step.
76
+
Next, you will run the Instruction Mix recipe to confirm where optimization opportunities exist and implement vectorization.
Copy file name to clipboardExpand all lines: content/learning-paths/servers-and-cloud-computing/performix-microarchitecture/3-instruction-mix.md
+43-20Lines changed: 43 additions & 20 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,44 +1,54 @@
1
1
---
2
-
title: Understand Instruction Mix
2
+
title: Analyze SIMD utilization with the Instruction Mix recipe
3
3
weight: 4
4
4
5
5
### FIXED, DO NOT MODIFY
6
6
layout: learningpathall
7
7
---
8
8
9
-
## Run Instruction Mix
9
+
## Run the Instruction Mix recipe
10
10
11
-
The previous CPU Microarchitecture analysis showed that the sample application used no single instruction, multiple data (SIMD) operations, which points to an optimization opportunity. Run the Instruction Mix recipe to learn more. The Instruction Mix launch panel is similar to CPU Microarchitecture, but it does not include options to choose metrics. Again, enter the full path to the workload. This Mandelbrot example is native C++ code, not Java or .NET, so you do not need to collect managed code stacks.
11
+
The previous CPU Microarchitecture analysis showed that the sample application used no single instruction, multiple data (SIMD) operations, which points to an optimization opportunity. Run the Instruction Mix recipe to learn more. The Instruction Mix launch panel is similar to CPU Microarchitecture, but it does not include options to choose metrics. Again, enter the full path to the workload.
The results below confirm a high number of integer and floating-point operations, with no SIMD operations. The **Insights** panel suggests vectorization as a path forward, lists possible root causes, and links to related Learning Paths.

19
20
20
-
## Vectorize
21
+
## Vectorize the application
21
22
22
-
The CPU Hotspots recipe in [Find CPU cycle hotspots with Arm Performix](/learning-paths/servers-and-cloud-computing/cpu_hotspot_performix/) helps you identify which functions consume the most time. In this example, `Mandelbrot::draw` and its inner function `Mandelbrot::getIterations`dominate runtime. A vectorized version is available in the [instruction-mix branch](https://github.com/arm-education/Mandelbrot-Example/tree/instruction-mix). This branch uses Neon operations for Neoverse N1, while your platform might support alternatives such as SVE or SVE2.
23
+
To address the lack of SIMD operations, you can vectorize the application's most intensive functions. For the Mandelbrot application, `Mandelbrot::draw` and its inner `Mandelbrot::getIterations`function consume most of the runtime. A vectorized version is available in the [instruction-mix branch](https://github.com/arm-education/Mandelbrot-Example/tree/instruction-mix). This branch uses Neon operations, which run on any Neoverse system. Your system might support alternatives such as SVE or SVE2 which can also be used.
23
24
24
-
After you rebuild the application and run Instruction Mix again, integer and floating-point operations are greatly reduced and replaced by a smaller set of SIMD instructions.
25
+
Connect to your target machine using SSH and navigate to your project directory. Because you modified `main.cpp` earlier, you must stash your changes before switching to the `instruction-mix` branch. Then, rebuild the application:
After you rebuild the application and run the Instruction Mix recipe again, integer and floating-point operations are greatly reduced and replaced by a smaller set of SIMD instructions.

42
52
Compared to the baseline, floating-point operations, branch operations, and some integer operations have been traded for loads, stores, and SIMD operations.
43
53
Execution time also improves significantly, making this run nearly four times faster.
44
54
@@ -60,10 +70,10 @@ user 0m8.331s
60
70
sys 0m0.016s
61
71
```
62
72
63
-
## CPU Microarchitecture results comparison
73
+
## Compare the CPU Microarchitecture results
64
74
65
75
The CPU Microarchitecture recipe also supports a **Compare** view that shows percentage-point changes in each stage and instruction type.

67
77
68
78
You can now see that Load and Store operations account for about 70% of execution time. **Insights** offers several explanations because multiple issues can contribute to the root cause.
69
79
```
@@ -78,7 +88,9 @@ POSSIBLE CAUSES
78
88
- Instruction dependencies that create pipeline bubbles
79
89
```
80
90
81
-
Next, add optimization flags to the compiler to enable more aggressive loop unrolling.
91
+
## Apply compiler optimizations for loop unrolling
92
+
93
+
To address the new load and store bottlenecks, add optimization flags to the compiler to enable more aggressive loop unrolling. Edit the `build.sh` script to include these flags in the `CXXFLAGS` array:
82
94
```bash
83
95
# build.sh
84
96
CXXFLAGS=(
@@ -92,7 +104,9 @@ Next, add optimization flags to the compiler to enable more aggressive loop unro
92
104
)
93
105
```
94
106
95
-
Runtime improves again, with an additional 11x speedup over the SIMD build that uses default compiler flags.
107
+
After saving the file, run `./build.sh` to compile the application with the new flags.
108
+
109
+
Runtime improves again, with an additional 11x speedup over the SIMD build that used the default compiler flags.
96
110
97
111
98
112
```bash { command_line="root@localhost | 2-6" }
@@ -105,8 +119,17 @@ sys 0m0.014s
105
119
```
106
120
107
121
Another CPU Microarchitecture measurement shows that Load and Store bottlenecks are almost eliminated. SIMD floating-point operations now dominate execution, which indicates the application is better tuned to feed floating-point execution units.

127
+
128
+
## What you've accomplished and what's next
129
+
130
+
In this section:
131
+
- You used the Instruction Mix recipe to confirm a lack of SIMD operations.
132
+
- You vectorized the sample application and verified the shift toward SIMD execution.
133
+
- You applied compiler loop unrolling to relieve backend load/store bottlenecks, achieving over 40x speedup.
134
+
135
+
You are now ready to analyze and optimize your own native C/C++ applications on Arm Neoverse using Arm Performix. Review the next steps to continue your learning journey.
0 commit comments