Skip to content

Commit 0c3f836

Browse files
committed
Tech review of GPT-2 and Performix
1 parent 89a37bd commit 0c3f836

7 files changed

Lines changed: 46 additions & 33 deletions

File tree

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

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

4+
draft: true
5+
cascade:
6+
draft: true
7+
48
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.
59

610
minutes_to_complete: 45
@@ -17,7 +21,7 @@ prerequisites:
1721
- Access to Arm Performix configured with a remote Arm Linux target. For setup, see the [Arm Performix install guide](/install-guides/performix/)
1822
- Basic understanding of C++ and compiler optimization
1923
- Basic understanding of matrix multiplication
20-
- Basic understanding of writing SIMD code with Neon and/or SVE.
24+
- Basic understanding of writing SIMD code with Neon or SVE
2125

2226
author:
2327
- Kieran Hejmadi
@@ -30,7 +34,7 @@ armips:
3034
- Neoverse
3135
tools_software_languages:
3236
- Arm Performix
33-
- C++
37+
- C
3438
- LLM
3539
- NEON
3640
- SVE

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
---
2-
title: Background
2+
title: Understand instruction mix profiling
33
weight: 2
44

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

9-
## What the instruction mix recipe shows
9+
## Instruction mix overview
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

@@ -25,20 +25,20 @@ The instruction mix result gives you two complementary views:
2525

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

28-
## Why instruction mix is useful
28+
## Detecting missed vectorization
2929

3030
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.
3131

3232
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.
3333

34-
## Why use a GPT-2 workload
34+
## GPT-2 as a test workload
3535

36-
In this Learning Path, you 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.
36+
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.
3737

3838
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)
3939

4040
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.
4141

42-
## What you've learned and what's next
42+
## Next steps
4343

44-
In this section, you learned what instruction mix represents and why it is useful for LLM inference optimization on Arm. Next, you will set up the GPT-2 example, build the binaries, and run a baseline test.
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.

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ git checkout tags/v0.0.2
2424

2525
The C++ runtime expects exported model binaries. Create a Python virtual environment, install dependencies, and export GPT-2 Medium weights and vocabulary:
2626

27-
This Learning Path uses [openai-community/gpt2-medium on Hugging Face](https://huggingface.co/openai-community/gpt2-medium), which corresponds to the GPT-2 Medium model from the original OpenAI GPT-2 release in 2019. The model has 355 million parameters, and in this workflow it runs with unquantized FP32 (32-bit floating-point) weights.
27+
The workload uses [openai-community/gpt2-medium on Hugging Face](https://huggingface.co/openai-community/gpt2-medium), which corresponds to the GPT-2 Medium model from the original OpenAI GPT-2 release in 2019. The model has 355 million parameters, and in this workflow it runs with unquantized FP32 (32-bit floating-point) weights.
2828

2929
```bash
3030
python3 -m venv venv
@@ -105,8 +105,10 @@ Run the scalar baseline binary:
105105
./build/gpt2 --model gpt2-medium "Once upon a time" -n 20
106106
```
107107

108+
The output is:
109+
108110
![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")
109111

110-
## What you've learned and what's next
112+
## Next steps
111113

112-
You now have a working baseline binary and model files. Next, you will use the Instruction Mix recipe in Arm Performix to inspect static disassembly and dynamic runtime behavior.
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.

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ 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 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.
1616

17-
```out
18-
<path to repository>/build/gpt2 --model gpt2-medium "Once upon a time" -n 150
17+
Copy and paste the command below as the Performix Workload. If your path is different adjust it to point to `gpt2`.
18+
19+
```console
20+
/home/ubuntu/GPT-2-Example/build/gpt2 --model gpt2-medium "Once upon a time" -n 150
1921
```
2022

2123
![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")
@@ -38,13 +40,16 @@ The Instruction Mix recipe helps fill this gap.
3840

3941
## Configure the Instruction Mix recipe
4042

41-
Open Arm Performix and select the **Instruction Mix** recipe. If this is your first run on the target, complete tool deployment as prompted.
43+
Open Arm Performix and select the **Instruction Mix** recipe.
44+
4245
Set the launch command to your baseline binary with the same runtime arguments used for baseline testing:
4346

44-
```output
45-
</path/to/GPT-2-Example>/build/gpt2 --model gpt2-medium "Once upon a time" -n 150`
47+
```console
48+
/home/ubuntu/GPT-2-Example/build/gpt2 --model gpt2-medium "Once upon a time" -n 150
4649
```
4750

51+
Note about number of counters.
52+
4853
Use the same model and prompt arguments as your baseline terminal run so the measurements are comparable.
4954

5055
![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")
@@ -63,6 +68,6 @@ Then inspect dynamic analysis bar chart to see where sampled runtime work is con
6368

6469
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.
6570

66-
## What you've learned and what's next
71+
## Next steps
6772

68-
You used Instruction Mix to confirm that baseline runtime is dominated by scalar-heavy `matmul` execution. Next, you will compare updated instruction mix and throughput across scalar, NEON, SVE, and KleidiAI variants.
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.

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

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

55
### FIXED, DO NOT MODIFY
@@ -66,6 +66,6 @@ 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-
## What you've learned and what's next
69+
## Next steps
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 will compare instruction mix and throughput across scalar, NEON, SVE, and KleidiAI variants.
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.

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ weight: 7
66
layout: learningpathall
77
---
88

9-
## Confirm instruction-mix changes after vectorization
9+
## Verify vectorization with instruction mix
1010

1111
In the earlier build step, you created `gpt2_neon` and `gpt2_sve`. These binaries use the reference solutions in `matmul_neon.cpp` and `matmul_sve.cpp`, respectively. Run the `gpt2_neon` binary with the following command to observe the speedup.
1212

@@ -56,7 +56,7 @@ For a full-page view, open [Godbolt session with all three matmul kernels](https
5656

5757
Run the provided comparison script to measure tokens per second across all available binaries:
5858

59-
```bash bash { command_line="user@host | 2-30"}
59+
```bash bash { command_line="ubuntu@ip | 2-30"}
6060
./compare_gpt2_variants.sh
6161
Model: gpt2-medium
6262
Prompt: Once upon a time
@@ -80,4 +80,4 @@ run 1: 3.04859 tok/s
8080
avg: 3.048590 tok/s
8181
```
8282

83-
These results show that intrinsics increase throughput from about 3 tok/s in the scalar baseline to about 13.9 tok/s with SVE. Next, you will use optimized libraries to push performance further.
83+
These results show that intrinsics increase throughput from about 3 tok/s in the scalar baseline to about 13.9 tok/s with SVE. Next, you will use optimized libraries to push performance further.

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ weight: 8
66
layout: learningpathall
77
---
88

9-
## Understand why KleidiAI improves matmul
9+
## KleidiAI packing and tiling
1010

1111
Getting close to peak matmul performance is complex, especially in transformer inference where one kernel runs many times per token. Raw SIMD intrinsics can accelerate execution, but higher-level algorithms can improve matrix multiplication through techniques such as packing and tiling.
1212

@@ -15,7 +15,7 @@ Getting close to peak matmul performance is complex, especially in transformer i
1515
- *Tiling* breaks large matrices into smaller blocks that fit better in cache, so data is reused more often and memory bandwidth pressure is lower.
1616
Arm created [KleidiAI](https://github.com/ARM-software/kleidiai) to provide the fastest Arm CPU microkernels on packing and tiled matrix multiplication so you can use these optimizations without writing and tuning every low-level kernel yourself.
1717

18-
## Trace the KleidiAI path in this GPT-2 example
18+
## KleidiAI integration in the GPT-2 example
1919

2020
The file `src/kernels/matmul_kai_sve.cpp` is the runtime bridge between your model code and a KleidiAI float32 SVE microkernel. Since packing is a preprocessing step, this workflow uses a slightly modified inference engine, `gpt2_kai_sve.cpp`. It builds a `kai_matmul_clamp_f32_f32_f32p_ukernel` function table and binds function pointers such as:
2121

@@ -27,7 +27,7 @@ The `kai_run_matmul_clamp_f32_f32_f32p4vlx1b_6x4vl_sve_mla` is our entry. As per
2727

2828
For more details on KleidiAI, please refer to the [official GitLab repository](https://gitlab.arm.com/kleidi/kleidiai).
2929

30-
## Read the source code: key steps
30+
## Key source code
3131

3232
All matmul implementations in `src/kernels/` follow the same high-level pattern from `matmul.h`: they compute float32 output from float32 input vectors and weights. The KleidiAI variant keeps this behavior but changes the RHS argument to a packed buffer (`const uint8_t* rhs_packed`) so the compute path can consume prepacked tiles.
3333

@@ -62,11 +62,11 @@ The execution flow is:
6262
In `src/gpt2_kai_sve.cpp`, runtime code prepares packed weights once with `kai_run_rhs_pack_kxn_x32p4vlx1b_x32_x32_sve`, stores them in `PackedWeights`, and calls `kernels::matmul_kai_sve(...)` at runtime. This is why the runtime file and `matmul_kai_sve.cpp` must match: the runtime produces packed buffers in the format expected by the same f32 ukernel family.
6363

6464

65-
## Compare SVE intrinsics and KleidiAI
65+
## SVE intrinsics versus KleidiAI
6666

6767
Run the comparison script from the repository root with the following command.
6868

69-
```bash { command_line="user@host | 2-30"}
69+
```bash { command_line="ubuntu@ip | 2-30"}
7070
./compare_gpt2_variants.sh kai
7171
Model: gpt2-medium
7272
Prompt: Once upon a time
@@ -103,7 +103,7 @@ avg: 3.047840 tok/s
103103
You can increase throughput by running the KleidiAI path with multiple matmul threads. For this 355M model, tune `--matmul-threads` heuristically on your target system to find the optimal value. For our Graviton 3 instance, we observe a max token generation speed of 34.5 token/s with 4 threads.
104104

105105

106-
``` bash { command_line="user@host | 3-50"}
106+
``` bash { command_line="ubuntu@ip | 3-50"}
107107
cd build
108108
./gpt2_kai_sve --model gpt2-medium "Once upon a time" -n 150 --matmul-threads 4
109109
Weights path: /home/ubuntu/GPT-2-DEMO/GPT-2-Example/models/gpt2-medium/weights.bin
@@ -139,4 +139,6 @@ They would be kicked out of the village, and they would suffer and die as punish
139139
140140
## Summary
141141
142-
In this Learning Path, you used Arm Performix Instruction Mix to detect scalar-heavy hot paths, validated vectorization changes with static and dynamic evidence, and compared baseline, Neon, SVE, and KleidiAI-backed matmul implementations. This workflow is transferable to your own codebase: use instruction mix to detect missed vectorization and other unexpected instruction-balance patterns, validate changes with static and runtime evidence, and then tune to meet your performance requirements.
142+
You used Arm Performix Instruction Mix to detect scalar-heavy hot paths, validated vectorization changes with static and dynamic evidence, and compared baseline, Neon, SVE, and KleidiAI-backed matmul implementations. Starting from roughly 3 tok/s with scalar code, you reached about 15 tok/s with SVE intrinsics and 35 tok/s with KleidiAI packing, tiling, and multithreading.
143+
144+
This workflow is transferable to your own codebase: use instruction mix to detect missed vectorization and other unexpected instruction-balance patterns, validate changes with static and runtime evidence, and then tune to meet your performance requirements.

0 commit comments

Comments
 (0)