Skip to content

Commit e905955

Browse files
authored
Merge pull request #5587 from MicrosoftDocs/main
5/31/2024 AM Publish
2 parents 18e0026 + a03ddd0 commit e905955

23 files changed

+346
-3
lines changed

docs/build-insights/index.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ landingContent:
2525
url: get-started-with-cpp-build-insights.md
2626
- linkListType: tutorial
2727
links:
28+
- text: Build Insights function view
29+
url: tutorials/build-insights-function-view.md
30+
- text: Build Insights included files view
31+
url: tutorials/build-insights-included-files-view.md
2832
- text: vcperf and Windows Performance Analyzer
2933
url: tutorials/vcperf-and-wpa.md
3034
- text: Windows Performance Analyzer basics

docs/build-insights/toc.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ items:
66
- name: "Tutorials"
77
expanded: true
88
items:
9+
- name: "Tutorial: Troubleshoot function inlining on build time"
10+
href: ../build-insights/tutorials/build-insights-function-view.md
11+
- name: "Tutorial: Troubleshoot header file impact on build time"
12+
href: ../build-insights/tutorials/build-insights-included-files-view.md
913
- name: "vcperf and Windows Performance Analyzer"
1014
href: ../build-insights/tutorials/vcperf-and-wpa.md
1115
- name: "Windows Performance Analyzer basics"
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
---
2+
title: "Tutorial: Troubleshoot function inlining on build time"
3+
description: "Tutorial on how to use Build Insights function view to troubleshoot the impact of function inlining on build time in your C++ projects."
4+
ms.date: 5/30/2024
5+
helpviewer_keywords: ["C++ Build Insights", "inline function analysis", "build time analysis", "__forceinline analysis", "inlines analysis"]
6+
---
7+
# Tutorial: Troubleshoot function inlining on build time
8+
9+
Use Build Insights **Functions** view to troubleshoot the impact of function inlining on build time in your C++ projects.
10+
11+
## Prerequisites
12+
13+
- Visual Studio 2022 17.8 or greater.
14+
- C++ Build insights is enabled by default if you install either the Desktop development with C++ workload or the Game development with C++ workload.
15+
16+
:::image type="complex" source="./media/installer-desktop-cpp-build-insights.png" alt-text="Screenshot of the Visual Studio Installer with the Desktop development with C++ workload selected.":::
17+
The list of installed components is shown. C++ Build Insights is highlighted and is selected which means it's installed.
18+
:::image-end:::
19+
20+
:::image type="complex" source="./media/installer-gamedev-cpp-build-insights.png" alt-text="Screenshot of the Visual Studio Installer with the Game development with C++ workload selected.":::
21+
The list of installed components is shown. C++ Build Insights is highlighted and is selected which means it's installed.
22+
:::image-end:::
23+
24+
## Overview
25+
26+
Build Insights, now integrated into Visual Studio, helps you optimize your build times--especially for large projects like AAA games. Build Insights provides analytics such as **Functions** view, which helps diagnose expensive code generation during build time. It displays the time it takes to generate code for each function, and shows the impact of [`__forceinline`](../../cpp/inline-functions-cpp.md#inline-__inline-and-__forceinline).
27+
28+
The `__forceinline` directive tells the compiler to inline a function regardless of its size or complexity. Inlining a function can improve runtime performance by reducing the overhead of calling the function. The tradeoff is that it can increase the size of the binary and impact your build times.
29+
30+
For optimized builds, the time spent generating code contributes significantly to the total build time. In general, C++ function optimization happens quickly. In exceptional cases, some functions can become large enough and complex enough to put pressure on the optimizer and noticeably slow down your builds.
31+
32+
In this article, learn how to use the Build Insights **Functions** view to find inlining bottlenecks in your build.
33+
34+
## Set build options
35+
36+
To measure the results of `__forceinline`, use a **Release** build because debug builds don't inline `__forceinline` since debug builds use the [`/Ob0`](../../build/reference/ob-inline-function-expansion.md) compiler switch, which disables that optimization. Set the build for **Release** and **x64**:
37+
38+
1. In the **Solution Configurations** dropdown, choose **Release**.
39+
1. In the **Solution Platforms** dropdown, choose **x64**.
40+
41+
:::image type="content" source="./media/build-options-release.png" alt-text="Screenshot of the Solution Configuration dropdown set to Release, and the Solution Platform dropdown set to x64.":::
42+
43+
Set the optimization level to maximum optimizations:
44+
45+
1. In the **Solution Explorer**, right-click the project name and select **Properties**.
46+
1. In the project properties, navigate to **C/C++** > **Optimization**.
47+
1. Set the **Optimization** dropdown to **Maximum Optimization (Favor Speed) ([`/O2`](../../build/reference/ob-inline-function-expansion.md))**.
48+
49+
:::image type="content" source="./media/max-optimization-setting.png" alt-text="Screenshot of the project property pages dialog. The settings are open to Configuration Properties > C/C++ > Optimization. The Optimization dropdown is set to Maximum Optimization (Favor Speed) (/O2).":::
50+
51+
1. Click **OK** to close the dialog.
52+
53+
## Run Build Insights
54+
55+
On a project of your choosing, and using the **Release** build options set in the previous section, run Build Insights by choosing from the main menu **Build** > **Run Build Insights on Selection** > **Rebuild**. You can also right-click a project in the solution explorer and choose **Run Build Insights** > **Rebuild**. Choose **Rebuild** instead of **Build** to measure the build time for the entire project and not for just the few files may be dirty right now.
56+
57+
:::image type="content" source="./media/build-insights-rebuild-project.png" alt-text="Screenshot of the main menu with Run Build Insights on Selection > Rebuild selected.":::
58+
59+
When the build finishes, an Event Trace Log (ETL) file opens. It's saved in the folder pointed to by the Windows `TEMP` environment variable. The generated name is based on the collection time.
60+
61+
## Function view
62+
63+
In the window for the ETL file, choose the **Functions** tab. It shows the functions that were compiled and the time it took to generate the code for each function. If the amount of code generated for a function is negligible, it won't appear in the list to avoid degrading build event collection performance.
64+
65+
:::image type="complex" source="./media/functions-view-before-fix.png" alt-text="Screenshot of the Build Insights Functions view file.":::
66+
In the Function Name column, performPhysicsCalculations() is highlighted and marked with a fire icon.:::
67+
:::image-end:::
68+
69+
The **Time [sec, %]** column shows how long it took to compile each function in [wall clock responsibility time (WCTR)](https://devblogs.microsoft.com/cppblog/faster-cpp-builds-simplified-a-new-metric-for-time/#:~:text=Today%2C%20we%E2%80%99d%20like%20to%20teach%20you%20about%20a,your%20build%2C%20even%20in%20the%20presence%20of%20parallelism). This metric distributes the wall clock time among functions based on their use of parallel compiler threads. For example, if two different threads are compiling two different functions simultaneously within a one-second period, each function’s WCTR is recorded as 0.5 seconds. This reflects each function’s proportional share of the total compilation time, taking into account the resources each consumed during parallel execution. Thus, WCTR provides a better measure of the impact each function has on the overall build time in environments where multiple compilation activities occur simultaneously.
70+
71+
The **Forceinline Size** column shows roughly how many instructions were generated for the function. Click the chevron before the function name to see the individual inlined functions that were expanded in that function how roughly how many instructions were generated for each.
72+
73+
You can sort the list by clicking on the **Time** column to see which functions are taking the most time to compile. A 'fire' icon indicates that cost of generating that function is high and is worth investigating. Excessive use of `__forceinline` functions can significantly slow compilation.
74+
75+
You can search for a specific function by using the **Filter Functions** box. If a function's code generation time is too small, it doesn't appear in the **Functions** View.
76+
77+
## Improve build time by adjusting function inlining
78+
79+
In this example, the `performPhysicsCalculations` function is taking the most time to compile.
80+
81+
:::image type="complex" source="./media/functions-view-before-fix.png" alt-text="Screenshot of the Build Insights Functions view.":::
82+
In the Function Name column, performPhysicsCalculations() is highlighted and marked with a fire icon.
83+
:::image-end:::
84+
85+
Investigating further, by selecting the chevron before that function, and then sorting the **Forceinline Size** column from highest to lowest, we see the biggest contributors to the problem.
86+
87+
:::image type="complex" source="./media/functions-view-expanded.png" alt-text="Screenshot of the Build Insights Functions view with an expanded function.":::
88+
performPhysicsCalculations() is expanded and shows a long list of functions that were inlined inside it. There are multiple instances of functions such as complexOperation(), recursiveHelper(), and sin() shown. The Forceinline Size column shows that complexOperation() is the largest inlined function at 315 instructions. recursiveHelper() has 119 instructions. Sin() has 75 instructions, but there are many more instances of it than the other functions.
89+
:::image-end:::
90+
91+
There are some larger inlined functions, such as `Vector2D<float>::complexOperation()` and `Vector2D<float>::recursiveHelper()` that are contributing to the problem. But there are many more instances (not all shown here) of `Vector2d<float>::sin(float)`, `Vector2d<float>::cos(float)`, `Vector2D<float>::power(float,int)`, and `Vector2D<float>::factorial(int)`. When you add those up, the total number of generated instructions quickly exceeds the few larger generated functions.
92+
93+
Looking at those functions in the source code, we see that execution time is going to be spent inside loops. For example, here's the code for `factorial()`:
94+
95+
```cpp
96+
static __forceinline T factorial(int n)
97+
{
98+
T result = 1;
99+
for (int i = 1; i <= n; ++i) {
100+
for (int j = 0; j < i; ++j) {
101+
result *= (i - j) / (T)(j + 1);
102+
}
103+
}
104+
return result;
105+
}
106+
```
107+
108+
Perhaps the overall cost of calling this function is insignificant compared to the cost of the function itself. Making a function inline is most beneficial when the time it takes to call the function (pushing arguments on the stack, jumping to the function, popping return arguments, and returning from the function) is roughly similar to the time it takes to execute the function, and when the function is called a lot. When that's not the case, there may be diminishing returns on making it inline. We can try removing the `__forceinline` directive from it to see if it helps the build time. The code for `power`, `sin()` and `cos()` is similar in that the code consists of a loop that will execute many times. We can try removing the `__forceinline` directive from those functions as well.
109+
110+
We rerun Build Insights from the main menu by choosing **Build** > **Run Build Insights on Selection** > **Rebuild**. You can also right-click a project in the solution explorer and choose **Run Build Insights** > **Rebuild**. We choose **Rebuild** instead of **Build** to measure the build time for the entire project, as before, and not for just the few files may be dirty right now.
111+
112+
The build time goes from 25.181 seconds to 13.376 seconds and the `performPhysicsCalculations` function doesn't show up anymore in the **Functions** view because it doesn't contribute enough to the build time to be counted.
113+
114+
:::image type="complex" source="./media/functions-view-after-fix.png" alt-text="Screenshot of the 2D vector header file.":::
115+
In the Function Name column, performPhysicsCalculations() is highlighted and marked with a fire icon.:::
116+
:::image-end:::
117+
118+
The Diagnostics Session time is the overall time it took do the build plus any overhead for gathering the Build Insights data.
119+
120+
The next step would be to profile the application to see if the performance of the application is negatively impacted by the change. If it is, we can selectively add `__forceinline` back as needed.
121+
122+
## Navigate to the source code
123+
124+
Double-click, right-click, or press **Enter** while on a file in the **Functions** view to open the source code for that file.
125+
126+
:::image type="content" source="./media/functions-view-goto-file.png" alt-text="Screenshot of a right-click on a file in the Functions view. The menu option Go To Source File is highlighted.":::
127+
128+
## Tips
129+
130+
- You can **File** > **Save As** the ETL file to a more permanent location to keep a record of the build time. You can then compare it to future builds to see if your changes are improving build time.
131+
- If you inadvertently close the Build Insights window, reopen it by finding the `<dateandtime>.etl` file in your temporary folder. The `TEMP` Windows environment variable provides the path of your temporary files folder.
132+
- To dig into the Build Insights data with Windows Performance Analyzer (WPA), click the **Open in WPA** button in the bottom right of the ETL window.
133+
- Drag columns to change the order of the columns. For instance, you may prefer moving the **Time** column to be the first column. You can hide columns by right-clicking on the column header and deselecting the columns you don't want to see.
134+
- The **Functions** view provides a filter box to find a function that you're interested in. It does partial matches on the name you provide.
135+
- If you forget how to interpret what the **Functions** view is trying to show you, hover over the tab to see a tooltip that describes the view. If you hover over the **Functions** tab, the tooltip says: "View that shows statistics for functions where the children nodes are force-inlined functions."
136+
137+
## Troubleshooting
138+
139+
- If the Build Insights window doesn't appear, do a rebuild instead of a build. The Build Insights window doesn't appear if nothing actually builds; which may be the case if no files changed since the last build.
140+
- If the Functions view doesn't show any functions, you may not be building with the right optimization settings. Ensure that you're building Release with full optimizations, as described in [Set build options](#set-build-options). Also, if a function's code generation time is too small, it doesn't appear in the list.
141+
142+
## See also
143+
144+
[Inline functions (C++)](../../cpp/inline-functions-cpp.md)\
145+
[Faster C++ builds, simplified: a new metric for time](https://devblogs.microsoft.com/cppblog/faster-cpp-builds-simplified-a-new-metric-for-time)\
146+
[Build Insights in Visual Studio video - Pure Virtual C++ 2023](/events/pure-virtual-cpp-2023/build-insights-in-visual-studio)\
147+
[Troubleshoot header file impact on build time](build-insights-included-files-view.md)\
148+
[Functions View for Build Insights in Visual Studio 2022 17.8](https://devblogs.microsoft.com/cppblog/functions-view-for-build-insights-in-visual-studio-2022-17-8/)\
149+
[Tutorial: vcperf and Windows Performance Analyzer](vcperf-and-wpa.md)\
150+
[Improving code generation time with C++ Build Insights](https://devblogs.microsoft.com/cppblog/improving-code-generation-time-with-cpp-build-insights)

0 commit comments

Comments
 (0)