Skip to content

Commit dd7e618

Browse files
first pass performix instruction mix
1 parent 46f49d9 commit dd7e618

7 files changed

Lines changed: 77 additions & 59 deletions

File tree

content/learning-paths/servers-and-cloud-computing/performix-instruction-mix/_index.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
---
22
title: Profile GPT-2 instruction mix with Arm Performix
33

4-
draft: true
5-
cascade:
6-
draft: true
7-
84
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.
95

106
minutes_to_complete: 45
117

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.
139

1410
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
1612
- Build and run the GPT-2 inference example on an Arm Linux server
1713
- Identify why matrix multiplication dominates runtime and how vectorization changes the instruction mix
1814
- Compare throughput and instruction mix across scalar, NEON, SVE, and KleidiAI implementations
1915

2016
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/).
2218
- Basic understanding of C++ and compiler optimization
2319
- Basic understanding of matrix multiplication
2420
- Basic understanding of writing SIMD code with Neon or SVE
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,44 @@
11
---
2-
title: Understand instruction mix profiling
2+
title: Understand profiling with Arm Performix Instruction Mix
33
weight: 2
44

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

9-
## Instruction mix overview
9+
## What the Arm Performix Instruction Mix recipe is
1010

1111
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.
1212

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:
1414

1515
- integer and floating-point arithmetic
1616
- memory loads and stores (including exclusive operations)
1717
- control flow instructions, such as branches and loops
1818
- specialized instructions, such as cryptographic operations
1919
- SIMD (Single Instruction, Multiple Data) instructions, including NEON (fixed 128-bit) and SVE (scalable vector length)
2020

21-
The instruction mix result gives you two complementary views:
21+
The Instruction Mix result gives you two complementary views:
2222

2323
- static analysis, which inspects compiled machine code without running it
2424
- dynamic analysis, which measures instruction usage during real execution
2525

2626
Together, these views help you verify whether architecture-specific features are actually active in hot code paths.
2727

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.
3129

3230
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.
3331

3432
## GPT-2 as a test workload
3533

3634
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.
3735

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.
3939

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
4141

42-
## Next steps
42+
You now know what instruction mix represents and why it matters for LLM inference optimization on Arm.
4343

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.

content/learning-paths/servers-and-cloud-computing/performix-instruction-mix/how-to-2.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Set up and run GPT-2 baseline
2+
title: Set up and run a GPT-2 baseline
33
weight: 3
44

55
### FIXED, DO NOT MODIFY
@@ -8,7 +8,9 @@ layout: learningpathall
88

99
## Prepare the environment
1010

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/).
1214

1315
Install build prerequisites and clone the GPT-2 example repository:
1416

@@ -109,6 +111,8 @@ The output is:
109111

110112
![Animated terminal output showing GPT-2 baseline inference running on Arm Linux, including generated text and the final tokens-per-second summary.#center](./gpt2-baseline.gif "GPT-2 baseline runtime output on Arm Linux")
111113

112-
## Next steps
114+
## What you've accomplished and what's next
115+
116+
You now have a working baseline binary and model files.
113117

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.
Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Profile with instruction mix
2+
title: Profile with the Arm Performix Instruction Mix recipe
33
weight: 4
44

55
### FIXED, DO NOT MODIFY
@@ -12,29 +12,29 @@ Before you optimize, identify where the application spends most of its time. Use
1212

1313
Open Arm Performix and select the **Code Hotspots** recipe. If this is your first run on the target, complete tool deployment as prompted.
1414

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.
1616

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`:
1818

1919
```console
2020
/home/ubuntu/GPT-2-Example/build/gpt2 --model gpt2-medium "Once upon a time" -n 150
2121
```
2222

2323
![Arm Performix Code Hotspots recipe configuration showing launch arguments for the GPT-2 baseline run with -n 150 to emphasize inference runtime.#center](./code_hotspot.webp "Code Hotspots recipe configuration for GPT-2 baseline")
2424

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()`.
2626

2727
![Arm Performix hotspot results table showing matmul_ref as the dominant runtime function during GPT-2 baseline inference.#center](./code_hotspot_results.webp "Hotspot results highlighting matmul_ref")
2828

2929
This confirms that matrix multiplication is the highest-impact optimization target.
3030

3131
## Assess compiler output
3232

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.
3434

3535
{{< 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}" >}}
3636

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.
3838

3939
The Instruction Mix recipe helps fill this gap.
4040

@@ -48,26 +48,30 @@ Set the launch command to your baseline binary with the same runtime arguments u
4848
/home/ubuntu/GPT-2-Example/build/gpt2 --model gpt2-medium "Once upon a time" -n 150
4949
```
5050

51-
Note about number of counters.
51+
Note the number of counters.
5252

5353
Use the same model and prompt arguments as your baseline terminal run so the measurements are comparable.
5454

5555
![Arm Performix recipe setup screen showing Instruction Mix recipe selected with launch settings configured for the GPT-2 baseline executable.#center](./configuring-performix.webp "Configure Arm Performix Instruction Mix recipe")
5656

5757
### Analyze static disassembly
5858

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.
6062

6163
![Arm Performix static disassembly view showing instruction category breakdown for GPT-2 hot paths, highlighting scalar-heavy sections in baseline matmul code.#center](./static_disassembly.webp "Static disassembly instruction classification")
6264

6365
### Dynamic analysis
6466

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.
6668

6769
![Arm Performix dynamic functions table showing most runtime samples in matmul-related functions for baseline GPT-2 inference.#center](./instruction_mix_dynamic_analysis.webp "Dynamic function sample distribution")
6870

6971
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.
7072

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.
7276

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.

content/learning-paths/servers-and-cloud-computing/performix-instruction-mix/how-to-4.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
---
2-
title: Optimize matmul with vector intrinsics
2+
title: (Optional) Optimize matmul with vector intrinsics
33
weight: 5
44

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

9-
## Complete the challenge (optional)
9+
## Complete the challenge
1010

1111
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.
1212

@@ -34,7 +34,7 @@ Example solutions are available in:
3434

3535
You can use `AGENTS.md` in the GPT-2 example repository for guided learning support.
3636

37-
### Use the Arm MCP Server with Performix (optional)
37+
### Use the Arm MCP Server with Performix
3838

3939
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`.
4040

@@ -66,6 +66,8 @@ Restart your coding assistant, then prompt it to run Performix Instruction Mix a
6666

6767
![Screenshot of a coding assistant prompt configured to use Arm MCP Server tools for running Performix recipes and analyzing matmul_user optimization opportunities in the GPT-2 workload.#center](./mcp-performix-prompt.webp "Coding assistant prompt for Performix analysis through Arm MCP Server")
6868

69-
## Next steps
69+
## What you've accomplished and what's next
7070

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

Comments
 (0)