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
Copy file name to clipboardExpand all lines: content/learning-paths/servers-and-cloud-computing/performix-instruction-mix/_index.md
+3-7Lines changed: 3 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,24 +1,20 @@
1
1
---
2
2
title: Profile GPT-2 instruction mix with Arm Performix
3
3
4
-
draft: true
5
-
cascade:
6
-
draft: true
7
-
8
4
description: Learn how to profile GPT-2 inference on Arm Neoverse with the Arm Performix Instruction Mix recipe, identify scalar versus vector execution patterns, and improve throughput with NEON, SVE, and KleidiAI kernels.
9
5
10
6
minutes_to_complete: 45
11
7
12
-
who_is_this_for: This is an introductory topic for developers who want to get started using the instruction mix recipe in Arm Performix through a practical example.
8
+
who_is_this_for: This is an introductory topic for developers who want to get started using the Arm Performix Instruction Mix recipe through a practical example.
13
9
14
10
learning_objectives:
15
-
- Explain how the Instruction Mix recipe combines static disassembly with runtime sampling to show execution behavior
11
+
- Understand how the Instruction Mix recipe combines static disassembly with runtime sampling to show execution behavior
16
12
- Build and run the GPT-2 inference example on an Arm Linux server
17
13
- Identify why matrix multiplication dominates runtime and how vectorization changes the instruction mix
18
14
- Compare throughput and instruction mix across scalar, NEON, SVE, and KleidiAI implementations
19
15
20
16
prerequisites:
21
-
- Access to Arm Performix configured with a remote Arm Linux target. For setup, see the [Arm Performix install guide](/install-guides/performix/)
17
+
- Access to Arm Performix configured with a remote Arm Linux target. For setup, see the [Arm Performix install guide](/install-guides/performix/).
22
18
- Basic understanding of C++ and compiler optimization
23
19
- Basic understanding of matrix multiplication
24
20
- Basic understanding of writing SIMD code with Neon or SVE
title: Understand profiling with Arm Performix Instruction Mix
3
3
weight: 2
4
4
5
5
### FIXED, DO NOT MODIFY
6
6
layout: learningpathall
7
7
---
8
8
9
-
## Instruction mix overview
9
+
## What the Arm Performix Instruction Mix recipe is
10
10
11
11
The Arm Performix Instruction Mix recipe shows the types and proportions of machine instructions your workload executes at runtime and in static analysis, so you can see how efficiently your code uses Arm CPU hardware resources.
12
12
13
-
The Instruction Mix recipe classifies each instruction into a group. The available groups depend on the Neoverse architecture version you are profiling. Therefore the categories you see may vary depending on the version of Arm Neoverse you are using. Typical categories include:
13
+
The Instruction Mix recipe classifies each instruction into a group. The available groups depend on the Neoverse architecture version you are profiling. Therefore the categories you see might vary depending on the version of Arm Neoverse you are using. Typical categories include:
14
14
15
15
- integer and floating-point arithmetic
16
16
- memory loads and stores (including exclusive operations)
17
17
- control flow instructions, such as branches and loops
18
18
- specialized instructions, such as cryptographic operations
19
19
- SIMD (Single Instruction, Multiple Data) instructions, including NEON (fixed 128-bit) and SVE (scalable vector length)
20
20
21
-
The instruction mix result gives you two complementary views:
21
+
The Instruction Mix result gives you two complementary views:
22
22
23
23
- static analysis, which inspects compiled machine code without running it
24
24
- dynamic analysis, which measures instruction usage during real execution
25
25
26
26
Together, these views help you verify whether architecture-specific features are actually active in hot code paths.
27
27
28
-
## Detecting missed vectorization
29
-
30
-
Instruction mix is useful when you need to confirm that performance-critical code uses Arm CPU features effectively. This is especially helpful when you are, for example, validating the effectiveness of compiler autovectorization.
28
+
Instruction Mix is useful when you need to confirm that performance-critical code uses Arm CPU features effectively. This is especially helpful when you are, for example, validating the effectiveness of compiler autovectorization.
31
29
32
30
For example, if a hot function is mostly scalar at runtime when you expected NEON or SVE activity, that often indicates missed vectorization opportunities. You can then focus optimization work on compiler flags, data layout, loop structure, and kernel implementation to improve throughput where it matters most.
33
31
34
32
## GPT-2 as a test workload
35
33
36
34
You can run the [GPT-2 Medium](https://huggingface.co/openai-community/gpt2-medium) model on a minimal C++ inference engine to analyze instruction mix and throughput. This model is available under a [modified MIT License](https://github.com/openai/gpt-2/blob/master/LICENSE). You will confirm that matrix multiplication (`matmul`) is the hot path, then compare how scalar, NEON, and SVE implementations change instruction behavior and token generation speed.
37
35
38
-
This example implements only the forward inference path, with no back propagation or training. You do not need to understand the full transformer architecture to complete this Learning Path. Familiarity with matrix multiplication is enough. For background on GPT-2, see the original 2019 paper, [Language Models are Unsupervised Multitask Learners](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf)
36
+
You'll implement only the forward inference path, with no back propagation or training. You don't need to understand the full transformer architecture to complete this Learning Path. Familiarity with matrix multiplication is enough. For background on GPT-2, see the original 2019 paper, [Language Models are Unsupervised Multitask Learners](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf).
37
+
38
+
You'll also try implementing your own `matmul` kernels that target NEON and SVE, then use instruction mix data to verify that these vector paths are active and improving throughput.
39
39
40
-
You will also try implementing your own `matmul` kernels that target NEON and SVE, then use instruction mix data to verify that these vector paths are active and improving throughput.
40
+
## What you've learned and what's next
41
41
42
-
## Next steps
42
+
You now know what instruction mix represents and why it matters for LLM inference optimization on Arm.
43
43
44
-
You now know what instruction mix represents and why it matters for LLM inference optimization on Arm. Next, you'll set up the GPT-2 example, build the binaries, and run a baseline test.
44
+
Next, you'll set up the GPT-2 example, build the binaries, and run a baseline test.
Copy file name to clipboardExpand all lines: content/learning-paths/servers-and-cloud-computing/performix-instruction-mix/how-to-2.md
+8-4Lines changed: 8 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,5 @@
1
1
---
2
-
title: Set up and run GPT-2 baseline
2
+
title: Set up and run a GPT-2 baseline
3
3
weight: 3
4
4
5
5
### FIXED, DO NOT MODIFY
@@ -8,7 +8,9 @@ layout: learningpathall
8
8
9
9
## Prepare the environment
10
10
11
-
Use an Arm Linux target, such as an Arm Neoverse cloud instance. The results in this Learning Path were collected on a Graviton 3 instance based on Neoverse V1 running Ubuntu 24.04 LTS. If you have not configured Arm Performix yet, complete setup and target connection using the [Arm Performix install guide](/install-guides/performix/).
11
+
Use an Arm Linux target, such as an Arm Neoverse cloud instance. The results in this Learning Path were collected on a Graviton 3-based instance based on Neoverse V1 running Ubuntu 24.04 LTS.
12
+
13
+
If you've not configured Arm Performix yet, complete setup and target connection using the [Arm Performix install guide](/install-guides/performix/).
12
14
13
15
Install build prerequisites and clone the GPT-2 example repository:
14
16
@@ -109,6 +111,8 @@ The output is:
109
111
110
112

111
113
112
-
## Next steps
114
+
## What you've accomplished and what's next
115
+
116
+
You now have a working baseline binary and model files.
113
117
114
-
You now have a working baseline binary and model files. Next, you'll use the Instruction Mix recipe in Arm Performix to inspect static disassembly and dynamic runtime behavior.
118
+
Next, you'll use the Instruction Mix recipe in Arm Performix to inspect static disassembly and dynamic runtime behavior.
title: Profile with the Arm Performix Instruction Mix recipe
3
3
weight: 4
4
4
5
5
### FIXED, DO NOT MODIFY
@@ -12,29 +12,29 @@ Before you optimize, identify where the application spends most of its time. Use
12
12
13
13
Open Arm Performix and select the **Code Hotspots** recipe. If this is your first run on the target, complete tool deployment as prompted.
14
14
15
-
Set the launch command to your baseline binary with the number of tokens (`-n`) set to 150 as per the command below. This value keeps startup overhead small compared to inference time, so the profile minimizes the time taken to load the model weights.
15
+
Set the launch command to your baseline binary with the number of tokens (`-n`) set to 150. This value keeps startup overhead small compared to inference time, so the profile minimizes the time taken to load the model weights.
16
16
17
-
Copy and paste the command below as the Performix Workload. If your path is different adjust it to point to `gpt2`.
17
+
Copy and paste the following command as the Performix Workload. If your path is different, adjust it to point to `gpt2`:
18
18
19
19
```console
20
20
/home/ubuntu/GPT-2-Example/build/gpt2 --model gpt2-medium "Once upon a time" -n 150
21
21
```
22
22
23
23

24
24
25
-
The results show that `kernels::matmul_ref()` is the hottest function. Double-clicking on the function with show which lines of source code the samples are mostly attributed to the accumulate step of `kernels::matmul_ref()`.
25
+
The results show that `kernels::matmul_ref()` is the hottest function. Double-clicking on the function shows which lines of source code the samples are mostly attributed to the accumulate step of `kernels::matmul_ref()`.
26
26
27
27

28
28
29
29
This confirms that matrix multiplication is the highest-impact optimization target.
30
30
31
31
## Assess compiler output
32
32
33
-
We can use online tools such as [Compiler Explorer](https://godbolt.org/) to conveniently see how this function is being compiled with the `-O2 -g` flags. The example below uses `GCC 12.1.0`. You can check your installed compiler version with the `g++ --version` command and select the corresponding version from the Compiler Explorer drop-down menu. The generated assembly may differ slightly across compiler versions.
33
+
You can use online tools such as [Compiler Explorer](https://godbolt.org/) to conveniently see how this function is being compiled with the `-O2 -g` flags. The following example uses `GCC 12.1.0`. You can check your installed compiler version with the `g++ --version` command and select the corresponding version from the Compiler Explorer drop-down menu. The generated assembly might differ slightly across compiler versions.
34
34
35
35
{{< godbolt width="100%" height="400px" mode="assembly" opt="-O2 -g" src="void matmul_ref(float *out, const float *x, const float *W, const float *b, int n_in, int n_out)\n{\n for (int i = 0; i < n_out; i++) {\n float acc = b ? b[i] : 0.f;\n const float *row = W + (unsigned long long)i * (unsigned long long)n_in;\n for (int j = 0; j < n_in; j++) {\n acc += row[j] * x[j];\n }\n out[i] = acc;\n }\n}" >}}
36
36
37
-
This view helps you spot missed vectorization opportunities. In an optimized build, you would expect the accumulation step to use SIMD instructions, for example `fmla v0.4s, v3.4s, v2.4s` with use of the vector register (`v0->v3`). However, assembly inspection has limitations. First, you need familiarity with SIMD mnemonics to recognize vectorized code. Second, this narrow snippet does not show whether changing compiler flags introduces regressions in other parts of the codebase. Third, and most importantly, this static view does not show which instructions in this function run most often on the CPU.
37
+
This view helps you spot missed vectorization opportunities. In an optimized build, you'd expect the accumulation step to use SIMD instructions, for example `fmla v0.4s, v3.4s, v2.4s` with use of the vector register (`v0->v3`). However, assembly inspection has limitations. First, you need familiarity with SIMD mnemonics to recognize vectorized code. Second, this narrow snippet doesn't show whether changing compiler flags introduces regressions in other parts of the codebase. Third, and most importantly, this static view doesn't show which instructions in this function run most often on the CPU.
38
38
39
39
The Instruction Mix recipe helps fill this gap.
40
40
@@ -48,26 +48,30 @@ Set the launch command to your baseline binary with the same runtime arguments u
48
48
/home/ubuntu/GPT-2-Example/build/gpt2 --model gpt2-medium "Once upon a time" -n 150
49
49
```
50
50
51
-
Note about number of counters.
51
+
Note the number of counters.
52
52
53
53
Use the same model and prompt arguments as your baseline terminal run so the measurements are comparable.
54
54
55
55

56
56
57
57
### Analyze static disassembly
58
58
59
-
After the run completes, review static disassembly first. This view is ordered by percentage contribution and provides a high-level profile of the application’s generated instruction stream. It can help you identify broad characteristics, such as whether the code is branch-heavy, dominated by memory operations, or making effective use of SIMD instructions. Use this static view to understand overall code generation patterns rather than to attribute performance to specific functions or source lines. Dynamic analysis is typically more relevant for optimization because it reflects the instructions that are actually executed at runtime.
59
+
After the run completes, review static disassembly first. This view is ordered by percentage contribution and provides a high-level profile of the application’s generated instruction stream. It can help you identify broad characteristics, such as whether the code is branch-heavy, dominated by memory operations, or making effective use of SIMD instructions.
60
+
61
+
Use this static view to understand overall code generation patterns rather than to attribute performance to specific functions or source lines. Dynamic analysis is typically more relevant for optimization because it reflects the instructions that are actually executed at runtime.
60
62
61
63

62
64
63
65
### Dynamic analysis
64
66
65
-
Then inspect dynamic analysis bar chart to see where sampled runtime work is concentrated. Dynamic data is typically more useful for optimization because it reflects actual execution behavior for your input, runtime settings, and call frequencies.
67
+
Then, inspect dynamic analysis bar chart to see where sampled runtime work is concentrated. Dynamic data is typically more useful for optimization because it reflects actual execution behavior for your input, runtime settings, and call frequencies.
66
68
67
69

68
70
69
71
Finally, in dynamic functions, you can break down operation types to individual functions. This is particularly useful when no single function dominates the profile, allowing you to inspect dynamic instruction patterns for specific functions.
70
72
71
-
## Next steps
73
+
## What you've accomplished and what's next
74
+
75
+
You've now used Instruction Mix to confirm that baseline runtime is dominated by scalar-heavy `matmul` execution.
72
76
73
-
You used Instruction Mix to confirm that baseline runtime is dominated by scalar-heavy `matmul` execution. Next, you'll compare updated instruction mix and throughput across scalar, NEON, SVE, and KleidiAI variants.
77
+
Next, you'll compare updated Instruction Mix and throughput across scalar, NEON, SVE, and KleidiAI variants.
Copy file name to clipboardExpand all lines: content/learning-paths/servers-and-cloud-computing/performix-instruction-mix/how-to-4.md
+7-5Lines changed: 7 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,12 +1,12 @@
1
1
---
2
-
title: Optimize matmul with vector intrinsics
2
+
title: (Optional) Optimize matmul with vector intrinsics
3
3
weight: 5
4
4
5
5
### FIXED, DO NOT MODIFY
6
6
layout: learningpathall
7
7
---
8
8
9
-
## Complete the challenge (optional)
9
+
## Complete the challenge
10
10
11
11
In this project, `src/kernels/matmul_user.cpp` is your editable implementation file. The baseline behavior in this file is scalar, and the build uses `-O2 -g`, so compiler optimization is enabled but vector hardware is still underused in the hot loop.
12
12
@@ -34,7 +34,7 @@ Example solutions are available in:
34
34
35
35
You can use `AGENTS.md` in the GPT-2 example repository for guided learning support.
36
36
37
-
### Use the Arm MCP Server with Performix (optional)
37
+
### Use the Arm MCP Server with Performix
38
38
39
39
You can also use an MCP-compatible coding assistant, such as GitHub Copilot or Codex, with the Arm MCP Server. This gives the assistant direct tool access to run Performix recipes on your remote Arm target and create a faster feedback loop while you iterate on `matmul_user`.
40
40
@@ -66,6 +66,8 @@ Restart your coding assistant, then prompt it to run Performix Instruction Mix a
66
66
67
67

68
68
69
-
## Next steps
69
+
## What you've accomplished and what's next
70
70
71
-
In this optional section, you implemented and profiled a custom `matmul_user` kernel using the same workflow you used for baseline analysis. Next, you'll compare instruction mix and throughput across scalar, NEON, SVE, and KleidiAI variants.
71
+
You've now optionally implemented and profiled a custom `matmul_user` kernel using the same workflow you used for baseline analysis.
72
+
73
+
Next, you'll compare instruction mix and throughput across scalar, NEON, SVE, and KleidiAI variants.
0 commit comments