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/laptops-and-desktops/win_profile_guided_optimisation/_index.md
+19-8Lines changed: 19 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,34 +1,37 @@
1
1
---
2
-
title: Optimize C++ performance with Profile-Guided Optimization and Google Benchmark
2
+
title: Optimize C++ applications on Windows on Arm using Profile-Guided Optimization
3
3
4
4
draft: true
5
5
cascade:
6
6
draft: true
7
7
8
-
minutes_to_complete: 15
8
+
minutes_to_complete: 30
9
9
10
-
who_is_this_for: Developers looking to optimize C++ performance on an Arm-based Windows device, based on runtime behavior.
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.
11
11
12
12
learning_objectives:
13
13
- Microbenchmark a function using Google Benchmark.
14
14
- Apply profile-guided optimization to build performance-tuned binaries for Windows on Arm.
15
+
- Measure and compare performance improvements from PGO-optimized builds.
15
16
16
17
prerequisites:
17
18
- Basic C++ understanding.
18
-
- Access to an Arm-based Windows machine.
19
+
- A Windows on Arm machine with [Visual Studio](/install-guides/vs-woa/) and the C++ desktop development tools installed.
Copy file name to clipboardExpand all lines: content/learning-paths/laptops-and-desktops/win_profile_guided_optimisation/how-to-1.md
+25-10Lines changed: 25 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,21 +6,36 @@ weight: 2
6
6
layout: learningpathall
7
7
---
8
8
9
-
###What is Profile-Guided Optimization (PGO) and how does it work?
9
+
## What is Profile-Guided Optimization?
10
10
11
-
Profile-Guided Optimization (PGO) is a compiler optimization technique that enhances program performance by utilizing real-world execution data. PGO typically involves a two-step process:
11
+
Profile-Guided Optimization (PGO) is a compiler optimization technique that enhances your program's performance by using real-world execution data. PGO works in two steps:
12
12
13
-
- First, compile the program to produce an instrumented binary that collects profiling data during execution;
14
-
- Second, recompile the program with an optimization profile, allowing the compiler to leverage the collected data to make informed optimization decisions. This approach identifies frequently executed paths — known as “hot” paths — and optimizes them more aggressively, while potentially reducing emphasis on less critical code paths.
13
+
First, you compile your program to produce an instrumented binary that collects profiling data during execution. Second, you recompile the program with this optimization profile, allowing the compiler to make informed optimization decisions based on the collected data.
15
14
16
-
### When should I use Profile-Guided Optimization?
15
+
This approach identifies frequently executed paths (known as "hot" paths) and optimizes them more aggressively, while reducing emphasis on less critical code paths.
17
16
18
-
PGO is particularly beneficial in the later stages of development when real-world workloads are available. It is especially useful for applications where performance is critical and runtime behavior is complex or data-dependent. For instance, consider optimizing “hot” functions that execute frequently. Doing so ensures that the most impactful parts of your code are optimized based on actual usage patterns.
17
+
## How does PGO improve performance?
19
18
20
-
### What are the limitations of Profile-Guided Optimization and when should I avoid it?
19
+
PGO enables several compiler optimizations that aren't possible with static analysis alone.
21
20
22
-
While PGO offers substantial performance benefits, it has limitations. The profiling data must accurately represent typical usage scenarios; otherwise, the optimizations may not deliver the desired performance improvements and could even degrade performance.
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.
23
22
24
-
Additionally, the process requires extra build steps, potentially increasing compile times for large codebases. Therefore, use PGO only on performance-critical sections that are heavily influenced by actual runtime behavior. PGO might not be ideal for early-stage development or applications with highly variable or unpredictable usage patterns.
23
+
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.
25
24
26
-
For further information, see the [MSVC documentation](https://learn.microsoft.com/en-us/cpp/build/profile-guided-optimizations?view=msvc-170) on enabling and using PGO.
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.
26
+
27
+
## When should you use Profile-Guided Optimization?
28
+
29
+
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.
30
+
31
+
For example, consider optimizing "hot" functions that execute frequently. By doing so, you ensure that the most impactful parts of your code are optimized based on actual usage patterns.
32
+
33
+
## Limitations of Profile-Guided Optimization
34
+
35
+
While PGO offers substantial performance benefits, it has some limitations to keep in mind.
36
+
37
+
Your profiling data must accurately represent typical usage scenarios. If it doesn't, the optimizations might not deliver the desired performance improvements and could even degrade performance.
38
+
39
+
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.
40
+
41
+
PGO might not be ideal for early-stage development or applications with highly variable or unpredictable usage patterns.
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.
10
10
11
-
Google Benchmark is a C++ library specifically designed for microbenchmarking – measuring the performance of small code snippets with high accuracy. Microbenchmarking is essential for identifying bottlenecks and optimizing critical sections, especially in performance-sensitive applications.
11
+
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.
12
12
13
-
Google Benchmark simplifies this process by providing a framework that manages iterations, times execution, and performs statistical analysis. This allows you to focus on the code being measured, rather than writing boilerplate or trying to prevent unwanted compiler optimizations manually.
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.
14
14
15
-
To use Google Benchmark, define a function that accepts a `benchmark::State&` parameter and iterate over it to perform the benchmarking. Register the function using the `BENCHMARK` macro and include `BENCHMARK_MAIN()` to generate the benchmark's entry point.
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.
16
16
17
-
Here's a basic example:
17
+
This example shows a basic benchmark that measures the time it takes to create an empty string:
18
18
19
19
```cpp
20
20
#include<benchmark/benchmark.h>
@@ -28,18 +28,18 @@ BENCHMARK(BM_StringCreation);
28
28
BENCHMARK_MAIN();
29
29
```
30
30
31
-
### Filtering and Preventing Compiler Optimizations
31
+
## Filter benchmarks and prevent compiler optimizations
32
32
33
33
Google Benchmark provides tools to ensure accurate measurements by preventing unintended compiler optimizations and allowing flexible benchmark selection.
34
34
35
-
1. **Preventing Optimizations**: Use `benchmark::DoNotOptimize(value);` to force the compiler to read and store a variable or expression, ensuring it is not optimized away.
36
-
37
-
2. **Filtering Benchmarks**: To run a specific subset of benchmarks, use the `--benchmark_filter` command-line option with a regular expression. For example:
35
+
To prevent the compiler from optimizing away your code, use `benchmark::DoNotOptimize(value);` to force the compiler to read and store a variable or expression. This ensures your benchmark actually measures what you intend to measure.
38
36
39
-
```bash
40
-
.\benchmark_binary --benchmark_filter=BM_String.*
41
-
```
42
-
43
-
This eliminates the need to repeatedly comment out lines of source code.
37
+
When you have multiple benchmarks, you can run a specific subset using the `--benchmark_filter` command-line option with a regular expression. This example runs all benchmarks that start with "BM_String":
44
38
45
-
For more detailed information and advanced usage, refer to the [official documentation](https://github.com/google/benchmark).
39
+
```console
40
+
.\benchmark_binary --benchmark_filter=BM_String.*
41
+
```
42
+
43
+
This approach eliminates the need to repeatedly comment out lines of source code when you want to focus on specific benchmarks.
44
+
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.
Copy file name to clipboardExpand all lines: content/learning-paths/laptops-and-desktops/win_profile_guided_optimisation/how-to-3.md
+36-21Lines changed: 36 additions & 21 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,38 +1,38 @@
1
1
---
2
-
title: Example operation
2
+
title: Create a baseline benchmark
3
3
weight: 4
4
4
5
5
### FIXED, DO NOT MODIFY
6
6
layout: learningpathall
7
7
---
8
8
9
-
## Optimizing costly division operations with Google Benchmark and PGO
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.
10
10
11
-
In this section, you'll learn how to use Google Benchmark and Profile-Guided Optimization to improve the performance of a simple division operation. This example demonstrates how even seemingly straightforward operationscan benefit from optimization techniques.
11
+
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.
12
12
13
-
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, we can potentially achieve significant performance improvements.
13
+
For this example, you'll use an Arm computer running Windows.
14
14
15
-
For this example, you can use an Arm computer running Windows.
15
+
## Install the required tools
16
16
17
-
##What tools are needed to run a Google Benchmark example on Windows?
17
+
### Install vcpkg and Google Benchmark
18
18
19
-
Download the [Arm GNU Toolchain](https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain) to install the prerequisite packages.
20
-
21
-
Next, install the static version of Google Benchmark for Arm64 via vcpkg. Run the following commands in Powershell as Administrator:
19
+
Run the following commands in PowerShell:
22
20
23
21
```console
24
-
cd C:\git
25
-
git clone https://github.com/microsoft/vcpkg.git
26
-
cd vcpkg
27
-
.\bootstrap-vcpkg.bat
28
-
.\vcpkg install benchmark:arm64-windows-static
22
+
iex (iwr -useb https://aka.ms/vcpkg-init.ps1)
23
+
cd $HOME
24
+
mkdir pgo-benchmark
25
+
cd pgo-benchmark
26
+
& "$HOME\.vcpkg\vcpkg.exe" new --application
27
+
& "$HOME\.vcpkg\vcpkg.exe" add port benchmark
28
+
& "$HOME\.vcpkg\vcpkg.exe" install
29
29
```
30
30
31
-
## Division example
31
+
## Create the division benchmark
32
32
33
33
Use an editor to copy and paste the C++ source code below into a file named `div_bench.cpp`.
34
34
35
-
This trivial example takes in a vector of 4096 32-bit integers and divides each element by a number. Importantly, the use of `benchmark/benchmark.h` introduces indirection since the divisor value is unknown at compile time, although it is visible in the source code as 1500.
35
+
This example takes a vector of 4096 32-bit integers and divides each element by a number. The key detail here is that the divisor value is passed through `s.range(0)`, making it unknown at compile time. This prevents the compiler from applying optimizations like strength reduction, which means PGO will have an opportunity to make a real difference.
36
36
37
37
```cpp
38
38
#include<benchmark/benchmark.h>
@@ -54,21 +54,34 @@ BENCHMARK(baseDiv)->Arg(1500)->Unit(benchmark::kMicrosecond); // value of 1500 i
54
54
BENCHMARK_MAIN();
55
55
```
56
56
57
-
To compile and run the microbenchmark on this function, you need to link with the correct libraries:
57
+
## Compile and run the baseline benchmark
58
+
59
+
Open an **ARM64 Native Tools Command Prompt** from the Windows Start menu and start PowerShell:
60
+
61
+
```console
62
+
powershell
63
+
```
64
+
65
+
Before compiling, set an environment variable to refer to the vcpkg installation directory:
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.
0 commit comments