Skip to content

Commit 62a23a8

Browse files
initial commit before technical review
1 parent 09f168f commit 62a23a8

19 files changed

Lines changed: 585 additions & 0 deletions
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
title: Profile GPT-2 instruction mix with Arm Performix
3+
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.
5+
6+
minutes_to_complete: 45
7+
8+
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.
9+
10+
learning_objectives:
11+
- Explain how the Instruction Mix recipe combines static disassembly with runtime sampling to show execution behavior
12+
- Build and run the GPT-2 inference example on an Arm Linux server
13+
- Identify why matrix multiplication dominates runtime and how vectorization changes the instruction mix
14+
- Compare throughput and instruction mix across scalar, NEON, SVE, and KleidiAI implementations
15+
16+
prerequisites:
17+
- Access to Arm Performix configured with a remote Arm Linux target. For setup, see the [Arm Performix install guide](/install-guides/performix/)
18+
- Basic understanding of C++ and compiler optimization
19+
- Basic understanding of matrix multiplication
20+
- Basic understanding of writing SIMD code with Neon and/or SVE.
21+
22+
author:
23+
- Kieran Hejmadi
24+
- Oliver Grainge
25+
26+
### Tags
27+
skilllevels: Introductory
28+
subjects: Performance and Architecture
29+
armips:
30+
- Neoverse
31+
tools_software_languages:
32+
- Arm Performix
33+
- C++
34+
- LLM
35+
- NEON
36+
- SVE
37+
operatingsystems:
38+
- Linux
39+
further_reading:
40+
- resource:
41+
title: Arm Performix User Guide
42+
link: https://developer.arm.com/documentation/110163/latest
43+
type: documentation
44+
- resource:
45+
title: Find code hotspots with Arm Performix
46+
link: /learning-paths/servers-and-cloud-computing/cpu_hotspot_performix/
47+
type: learning-path
48+
- resource:
49+
title: Identify code hotspots using Arm Performix through the Arm MCP Server
50+
link: /learning-paths/servers-and-cloud-computing/performix-mcp-agent/
51+
type: learning-path
52+
- resource:
53+
title: Arm MCP Server GitHub Repository
54+
link: https://github.com/arm/mcp
55+
type: website
56+
- resource:
57+
title: GPT-2 Example repository
58+
link: https://github.com/arm-education/GPT-2-Example
59+
type: website
60+
61+
62+
63+
### FIXED, DO NOT MODIFY
64+
# ================================================================================
65+
weight: 1 # _index.md always has weight of 1 to order correctly
66+
layout: "learningpathall" # All files under learning paths have this same wrapper
67+
learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content.
68+
---
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
# ================================================================================
3+
# FIXED, DO NOT MODIFY THIS FILE
4+
# ================================================================================
5+
weight: 21 # The weight controls the order of the pages. _index.md always has weight 1.
6+
title: "Next Steps" # Always the same, html page title.
7+
layout: "learningpathall" # All files under learning paths have this same wrapper for Hugo processing.
8+
---
63.7 KB
Loading
44.1 KB
Loading
63.8 KB
Loading
64.8 KB
Loading
85 KB
Loading
49.9 KB
Loading
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
title: Background
3+
weight: 2
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## What the instruction mix recipe shows
10+
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+
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:
14+
15+
- integer and floating-point arithmetic
16+
- memory loads and stores (including exclusive operations)
17+
- control flow instructions, such as branches and loops
18+
- specialized instructions, such as cryptographic operations
19+
- SIMD (Single Instruction, Multiple Data) instructions, including NEON (fixed 128-bit) and SVE (scalable vector length)
20+
21+
The instruction mix result gives you two complementary views:
22+
23+
- static analysis, which inspects compiled machine code without running it
24+
- dynamic analysis, which measures instruction usage during real execution
25+
26+
Together, these views help you verify whether architecture-specific features are actually active in hot code paths.
27+
28+
## Why instruction mix is useful
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.
31+
32+
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+
34+
## Why use a GPT-2 workload
35+
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.
37+
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)
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.
41+
42+
## What you've learned and what's next
43+
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.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
title: Set up and run GPT-2 baseline
3+
weight: 3
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## Prepare the environment
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/).
12+
13+
Install build prerequisites and clone the GPT-2 example repository:
14+
15+
```bash
16+
sudo apt update
17+
sudo apt install -y git g++ cmake python3 python3-venv
18+
git clone --recurse-submodules https://github.com/arm-education/GPT-2-Example.git
19+
cd GPT-2-Example
20+
git checkout tags/v0.0.2
21+
```
22+
23+
## Export GPT-2 model assets
24+
25+
The C++ runtime expects exported model binaries. Create a Python virtual environment, install dependencies, and export GPT-2 Medium weights and vocabulary:
26+
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.
28+
29+
```bash
30+
python3 -m venv venv
31+
source venv/bin/activate
32+
pip install -r src/requirements.txt
33+
python3 src/export_gpt2.py --model gpt2-medium
34+
```
35+
36+
This creates:
37+
38+
- `models/gpt2-medium/weights.bin`
39+
- `models/gpt2-medium/vocab.bin`
40+
41+
## Review the source code
42+
43+
The `src/gpt2.cpp` file implements the end-to-end GPT-2 inference loop. Each generated token triggers a forward pass over all 24 transformer layers. Inside each layer, `matmul` is called multiple times: for the query/key/value projection, the attention output projection, and both feed-forward layers. It is called once more at the end for logits projection over the vocabulary:
44+
45+
```cpp
46+
// Attention QKV projection
47+
matmul(s.qkv.data(), s.xb.data(),
48+
w.c_attn_w.data()+(size_t)l*3*E*E,
49+
w.c_attn_b.data()+(size_t)l*3*E, E, 3*E);
50+
51+
// FFN expand
52+
matmul(s.mlp_h.data(), s.xb.data(),
53+
w.mlp_fc_w.data()+(size_t)l*4*E*E,
54+
w.mlp_fc_b.data()+(size_t)l*4*E, E, 4*E);
55+
56+
// Logits projection (vocab_size x n_embd)
57+
matmul(s.logits.data(), s.x.data(), w.wte.data(), nullptr, E, cfg.vocab_size);
58+
```
59+
60+
The `matmul` dispatch in `gpt2.cpp` selects a kernel at compile time based on a preprocessor flag:
61+
62+
```cpp
63+
static void matmul(float *out, const float *x, const float *W, const float *b,
64+
int n_in, int n_out) {
65+
#if defined(GPT2_KERNEL_NEON)
66+
kernels::matmul_neon(out, x, W, b, n_in, n_out);
67+
#elif defined(GPT2_KERNEL_SVE)
68+
kernels::matmul_sve(out, x, W, b, n_in, n_out);
69+
#elif defined(GPT2_KERNEL_USER)
70+
kernels::matmul_user(out, x, W, b, n_in, n_out);
71+
#else
72+
kernels::matmul_ref(out, x, W, b, n_in, n_out);
73+
#endif
74+
}
75+
```
76+
77+
The baseline kernel (`src/kernels/matmul_ref.cpp`) is a straightforward scalar nested for loop: for each output row, it walks the weight matrix row and accumulates a dot product with the input vector:
78+
79+
```cpp
80+
void matmul_ref(float *out, const float *x, const float *W, const float *b,
81+
int n_in, int n_out) {
82+
for (int i = 0; i < n_out; i++) {
83+
float acc = b ? b[i] : 0.f;
84+
const float *row = W + (size_t)i * n_in;
85+
for (int j = 0; j < n_in; j++) acc += row[j] * x[j];
86+
out[i] = acc;
87+
}
88+
}
89+
```
90+
91+
This scalar implementation can leave NEON and SVE vector units underused if the compiler cannot efficiently autovectorize it. Because `matmul` is called hundreds of times per token, explicitly optimizing this kernel guarantees SIMD execution where most of the available compute is spent.
92+
93+
## Build and run the baseline
94+
95+
Configure and build the project with CMake. The project uses `-O2 -g`, which keeps optimization enabled while preserving debug symbols for profiling.
96+
97+
```bash
98+
cmake -S . -B build -DBUILD_USER_MATMUL=ON
99+
cmake --build build --parallel
100+
```
101+
102+
Run the scalar baseline binary:
103+
104+
```bash
105+
./build/gpt2 --model gpt2-medium "Once upon a time" -n 20
106+
```
107+
108+
![Animated terminal output showing GPT-2 baseline inference running on Arm Linux, including generated text and the final tokens-per-second summary used for baseline comparison.#center](./gpt2-baseline.gif "GPT-2 baseline runtime output on Arm Linux")
109+
110+
## What you've learned and what's next
111+
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.

0 commit comments

Comments
 (0)