Skip to content

Commit ec46f03

Browse files
Merge branch 'ArmDeveloperEcosystem:main' into nvda
2 parents 8a1a77e + 55f0491 commit ec46f03

5 files changed

Lines changed: 70 additions & 38 deletions

File tree

content/learning-paths/laptops-and-desktops/win_profile_guided_optimisation/_index.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
---
22
title: Optimize C++ applications on Windows on Arm using Profile-Guided Optimization
33

4-
draft: true
5-
cascade:
6-
draft: true
7-
84
minutes_to_complete: 30
95

10-
who_is_this_for: This is an introductory topic for software developers who want to optimize C++ application performance on Windows on Arm using profile-guided optimization.
6+
who_is_this_for: This is an introductory topic for software developers who want to optimize C++ application performance on Windows on Arm using Profile-Guided Optimization (PGO).
117

128
learning_objectives:
13-
- Microbenchmark a function using Google Benchmark.
14-
- Apply profile-guided optimization to build performance-tuned binaries for Windows on Arm.
15-
- Measure and compare performance improvements from PGO-optimized builds.
9+
- Microbenchmark a function using Google Benchmark
10+
- Apply profile-guided optimization to build performance-tuned binaries for Windows on Arm
11+
- Measure and compare performance improvements from PGO-optimized builds
1612

1713
prerequisites:
18-
- Basic C++ understanding.
19-
- A Windows on Arm machine with [Visual Studio](/install-guides/vs-woa/) and the C++ desktop development tools installed.
14+
- Familiarity with C++ development and compiling programs from the command line
15+
- A Windows on Arm machine with [Visual Studio](/install-guides/vs-woa/) and the C++ desktop development tools installed
2016

2117
author: Tom Dunkle
2218

@@ -47,7 +43,7 @@ further_reading:
4743
link: https://learn.microsoft.com/en-us/windows/arm/overview
4844
type: documentation
4945
- resource:
50-
title: Arm Performance optimization resources
46+
title: Arm performance optimization resources
5147
link: https://learn.arm.com/learning-paths/laptops-and-desktops/
5248
type: website
5349

content/learning-paths/laptops-and-desktops/win_profile_guided_optimisation/how-to-1.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Profile-Guided Optimization
2+
title: Understand Profile-Guided Optimization
33
weight: 2
44

55
### FIXED, DO NOT MODIFY
@@ -14,17 +14,17 @@ First, you compile your program to produce an instrumented binary that collects
1414

1515
This approach identifies frequently executed paths (known as "hot" paths) and optimizes them more aggressively, while reducing emphasis on less critical code paths.
1616

17-
## How does PGO improve performance?
17+
## How PGO improves performance on Windows on Arm
1818

1919
PGO enables several compiler optimizations that aren't possible with static analysis alone.
2020

21-
The compiler arranges frequently executed code together in memory through code layout optimization, which improves instruction cache utilization and reduces branch mispredictions. Instead of using heuristics for inlining decisions, the compiler inlines functions based on actual call frequency and execution patterns from your profiling data.
21+
Code layout optimization arranges frequently executed code together in memory, improving instruction cache utilization and reducing branch mispredictions. Instead of using heuristics for inlining decisions, the compiler inlines functions based on actual call frequency and execution patterns from your profiling data.
2222

2323
For C++ virtual functions, PGO can identify the most common call targets and optimize or devirtualize those paths. The compiler can also eliminate dead code by optimizing differently or removing code paths that never execute in your profiling runs.
2424

25-
For example, if your application has an error handling path that rarely executes, PGO ensures the compiler doesn't optimize for that path at the expense of your main execution flow. The result is typically 5-15% performance improvement, though gains vary by application.
25+
If your application has an error handling path that rarely executes, PGO ensures the compiler doesn't optimize for that path at the expense of your main execution flow. Performance improvements typically range from 5-15%, though results vary by workload and architecture.
2626

27-
## When should you use Profile-Guided Optimization?
27+
## When to use PGO on Arm
2828

2929
You'll find PGO particularly beneficial in the later stages of development when real-world workloads are available. It's especially useful for applications where performance is critical and runtime behavior is complex or data-dependent.
3030

@@ -39,3 +39,7 @@ Your profiling data must accurately represent typical usage scenarios. If it doe
3939
Additionally, the process requires extra build steps, which can increase compile times for large codebases. Use PGO on performance-critical sections that are heavily influenced by actual runtime behavior.
4040

4141
PGO might not be ideal for early-stage development or applications with highly variable or unpredictable usage patterns.
42+
43+
## What you've accomplished and what's next
44+
45+
You now understand what PGO is, how it improves performance through code layout and inlining optimizations, and when to apply it. In the next section, you'll learn about Google Benchmark, the tool you'll use to measure these performance improvements.

content/learning-paths/laptops-and-desktops/win_profile_guided_optimisation/how-to-2.md

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

9+
## Overview
10+
911
Before you start working with Profile-Guided Optimization, you need to understand how to measure performance. This section introduces Google Benchmark, the tool you'll use to measure the impact of your optimizations. Don't worry about installing anything yet. You'll set up your environment and run your first benchmark in the next section.
1012

13+
## What is Google Benchmark?
14+
1115
Google Benchmark is a C++ library specifically designed for microbenchmarking, which means measuring the performance of small code snippets with high accuracy. Microbenchmarking helps you identify bottlenecks and optimize critical sections, especially in performance-sensitive applications.
1216

13-
Google Benchmark simplifies this process by providing a framework that manages iterations, times execution, and performs statistical analysis. This lets you focus on the code being measured, rather than writing test code or trying to prevent unwanted compiler optimizations manually.
17+
Google Benchmark simplifies this process by providing a framework that manages iterations, times execution, and performs statistical analysis. You can focus on the code being measured, rather than writing test code or trying to prevent unwanted compiler optimizations manually.
1418

15-
To use Google Benchmark, you define a function that accepts a `benchmark::State&` parameter and iterate over it to perform the benchmarking. You register the function using the `BENCHMARK` macro and include `BENCHMARK_MAIN()` to generate the benchmark's entry point.
19+
## Write a simple benchmark
1620

17-
This example shows a basic benchmark that measures the time it takes to create an empty string:
21+
To use Google Benchmark, define a function that accepts a benchmark::State& parameter and use it to run the benchmark in a loop. You register the function using the `BENCHMARK` macro and include `BENCHMARK_MAIN()` to generate the benchmark's entry point.
22+
23+
The following example shows a basic benchmark that measures the time it takes to create an empty string. A minimal benchmark looks like this:
1824

1925
```cpp
2026
#include <benchmark/benchmark.h>
@@ -28,7 +34,7 @@ BENCHMARK(BM_StringCreation);
2834
BENCHMARK_MAIN();
2935
```
3036
31-
## Filter benchmarks and prevent compiler optimizations
37+
## Control benchmark execution
3238
3339
Google Benchmark provides tools to ensure accurate measurements by preventing unintended compiler optimizations and allowing flexible benchmark selection.
3440
@@ -40,6 +46,8 @@ When you have multiple benchmarks, you can run a specific subset using the `--be
4046
.\benchmark_binary --benchmark_filter=BM_String.*
4147
```
4248

43-
This approach eliminates the need to repeatedly comment out lines of source code when you want to focus on specific benchmarks.
49+
Filtering eliminates the need to repeatedly comment out lines of source code when you want to focus on specific benchmarks.
50+
51+
## What you've accomplished and what's next
4452

45-
Now that you understand how Google Benchmark works, you're ready to install it and run your first benchmark. In the next section, you'll set up your environment and use Google Benchmark with Profile-Guided Optimization to measure and improve performance.
53+
You now understand how to write basic benchmarks with Google Benchmark, use `benchmark::DoNotOptimize` to prevent unwanted compiler optimizations, and filter benchmark execution with command-line options. In the next section, you'll install Google Benchmark and create a baseline benchmark to measure division performance on Windows on Arm.

content/learning-paths/laptops-and-desktops/win_profile_guided_optimisation/how-to-3.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@ weight: 4
66
layout: learningpathall
77
---
88

9-
In this section, you'll create a baseline benchmark to measure the performance of a division operation. This baseline will help you see the improvement when you apply Profile-Guided Optimization in the next section. This example demonstrates how even seemingly straightforward operations can benefit from optimization techniques.
9+
## Overview
10+
11+
In this section, you'll create a baseline benchmark to measure the performance of a division operation. This baseline allows you to measure the improvement when you apply profile-guided optimization in the next section.
1012

1113
Integer division is ideal for benchmarking because it's significantly more expensive than operations like addition, subtraction, or multiplication. On most CPU architectures, including Arm, division instructions have higher latency and lower throughput compared to other arithmetic operations. By applying Profile-Guided Optimization to code containing division operations, you can achieve significant performance improvements.
1214

1315
For this example, you'll use an Arm computer running Windows.
1416

15-
## Install the required tools
17+
## Set up Google Benchmark on Windows on Arm
18+
19+
Before you can run benchmarks, you need to install vcpkg (a C++ package manager) and Google Benchmark. This is a one-time setup step.
1620

1721
### Install vcpkg and Google Benchmark
1822

19-
Run the following commands in PowerShell:
23+
The following commands download and initialize vcpkg, create a project directory, and install Google Benchmark for Windows on Arm:
2024

2125
```console
2226
iex (iwr -useb https://aka.ms/vcpkg-init.ps1)
@@ -54,27 +58,29 @@ BENCHMARK(baseDiv)->Arg(1500)->Unit(benchmark::kMicrosecond); // value of 1500 i
5458
BENCHMARK_MAIN();
5559
```
5660
57-
## Compile and run the baseline benchmark
61+
## Compile the baseline benchmark with MSVC
5862
5963
Open an **ARM64 Native Tools Command Prompt** from the Windows Start menu and start PowerShell:
6064
6165
```console
6266
powershell
6367
```
6468

65-
Before compiling, set an environment variable to refer to the vcpkg installation directory:
69+
Set an environment variable to refer to the vcpkg-installed package directory for the ARM64 Windows target. This simplifies the compiler commands that follow:
6670

6771
```console
6872
$VCPKG="$HOME\pgo-benchmark\vcpkg_installed\arm64-windows"
6973
```
7074

71-
Now compile the benchmark. This command uses the MSVC compiler and links with the Google Benchmark libraries:
75+
Compile the benchmark. This command uses the MSVC compiler and links with the Google Benchmark libraries:
7276

7377
```console
7478
cl /I"$VCPKG\include" /D BENCHMARK_STATIC_DEFINE div_bench.cpp /link /LIBPATH:"$VCPKG\lib" benchmark.lib benchmark_main.lib shlwapi.lib
7579
```
7680

77-
Run the program to establish your baseline performance:
81+
## Run the benchmark
82+
83+
Add the vcpkg binary directory to your PATH so the program can find required DLLs, then run the benchmark:
7884

7985
```console
8086
$env:PATH += ";$HOME\pgo-benchmark\vcpkg_installed\arm64-windows\bin"
@@ -84,7 +90,7 @@ $env:PATH += ";$HOME\pgo-benchmark\vcpkg_installed\arm64-windows\bin"
8490
The output is similar to:
8591

8692
```output
87-
Running ./div_bench.base
93+
Running ./div_bench.exe
8894
Run on (4 X 2100 MHz CPU s)
8995
CPU Caches:
9096
L1 Data 64 KiB (x4)
@@ -99,4 +105,8 @@ Benchmark Time CPU Iterations
99105
baseDiv/1500 7.90 us 7.90 us 88512
100106
```
101107

102-
The baseline shows an average execution time of 7.90 microseconds. This gives you a clear starting point to measure improvement. Now that you have this baseline measurement, you're ready to apply Profile-Guided Optimization. In the next section, you'll use PGO to optimize this code and see how much faster it can run.
108+
The warning appears because the Google Benchmark library was built in debug mode, but it doesn't affect the validity of the measurements for this example.
109+
110+
## What you've accomplished and what's next
111+
112+
You've set up Google Benchmark on Windows on Arm, created a division-heavy benchmark, and established a baseline performance measurement of 7.90 microseconds. This baseline gives you a clear reference point to measure the impact of Profile-Guided Optimization. In the next section, you'll apply PGO to this code and measure the performance improvement.

content/learning-paths/laptops-and-desktops/win_profile_guided_optimisation/how-to-4.md

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

9+
## Overview
10+
911
Now that you have a baseline benchmark, you're ready to apply Profile-Guided Optimization. The PGO process involves three steps: build an instrumented binary, run it to collect profile data, and rebuild with optimizations based on that data.
1012

11-
## Build the instrumented binary
13+
## Build instrumented binary with MSVC
14+
15+
Open an **ARM64 Native Tools Command Prompt** from the Windows Start menu and start PowerShell if it's not already open. If you're starting a new session, navigate to your project directory and set the `$VCPKG` environment variable again:
1216

13-
You should already have an ARM64 Native Tools Command Prompt open with PowerShell running from the previous section.
17+
```console
18+
powershell
19+
cd $HOME\pgo-benchmark
20+
$VCPKG="$HOME\pgo-benchmark\vcpkg_installed\arm64-windows"
21+
```
1422

1523
Build the instrumented binary with the `/GENPROFILE` flag. This creates a version of your program that records how it executes:
1624

@@ -20,7 +28,7 @@ cl /O2 /GL /D BENCHMARK_STATIC_DEFINE /I "$VCPKG\include" /Fe:div_bench.exe div_
2028

2129
This command uses several important compiler and linker options. The `/O2` flag creates fast code, while `/GL` enables whole program optimization. The `/GENPROFILE` linker option generates a `.pgd` file for PGO, and `/LTCG` specifies link time code generation. The `/PGD` option specifies the database file where profile data will be stored.
2230

23-
## Collect profile data
31+
## Collect PGO profile data on Windows on Arm
2432

2533
Run the instrumented binary to generate profile data:
2634

@@ -30,7 +38,7 @@ Run the instrumented binary to generate profile data:
3038

3139
This execution creates profile data files (typically with a `.pgc` extension) in the same directory. The profile data captures information about which code paths execute most frequently and how the program behaves at runtime.
3240

33-
## Rebuild with optimizations
41+
## Rebuild with PGO optimizations
3442

3543
Now recompile the program using the `/USEPROFILE` flag to apply optimizations based on the collected data:
3644

@@ -40,7 +48,7 @@ cl /O2 /GL /D BENCHMARK_STATIC_DEFINE /I "$VCPKG\include" /Fe:div_bench_opt.exe
4048

4149
The `/USEPROFILE` linker option instructs the linker to enable PGO with the profile generated during the previous run. The compiler can now make informed decisions about code layout, inlining, and other optimizations based on actual runtime behavior.
4250

43-
## Measure the improvement
51+
## Measure PGO performance gains
4452

4553
Run the optimized binary to see the performance improvement:
4654

@@ -66,6 +74,12 @@ Benchmark Time CPU Iterations
6674
baseDiv/1500 2.86 us 2.86 us 244429
6775
```
6876

69-
The average execution time is reduced from 7.90 to 2.86 microseconds, which is a 64% improvement. This significant gain occurs because the profile data informed the compiler that the input divisor was consistently 1500 during the profiled runs, allowing it to apply specific optimizations that wouldn't be possible with static analysis alone.
77+
The warning appears because the Google Benchmark library was built in debug mode, but it doesn't affect the validity of the measurements.
78+
79+
The average execution time is reduced from 7.90 to 2.86 microseconds, which is a 64% improvement. This result was measured on a Windows on Arm device with Visual Studio 2022 (MSVC 17.0) using the division benchmark with a constant divisor of 1500. Your results may vary depending on your specific hardware and workload.
80+
81+
The compiler used the profile data to determine that the divisor was consistently 1500, enabling optimizations that wouldn't be possible with static analysis alone.
82+
83+
## What you've accomplished
7084

71-
You've successfully used Profile-Guided Optimization to improve performance on Windows on Arm. This same technique can be applied to your own performance-critical code to achieve similar improvements.
85+
You've applied PGO to reduce execution time by 64% on a division-heavy benchmark. You completed the full PGO workflow: instrument, profile, and optimize. Apply this same technique to performance-critical sections of your own code to achieve similar gains on Windows on Arm.

0 commit comments

Comments
 (0)