Skip to content

Commit 8c00a41

Browse files
committed
Merged main into live
2 parents 61479a1 + ebb777a commit 8c00a41

File tree

6 files changed

+63
-0
lines changed

6 files changed

+63
-0
lines changed

docs/profiling/cpu-usage.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ You can click the **Expand Hot Path** and **Show Hot Path** buttons to see the f
160160
|![Step 3](../profiling/media/procguid_3.png "ProcGuid_3")|The children of the second-level node are the user-code methods and asynchronous routines that are called or created by the second-level system and framework code.|
161161
|![Step 4](../profiling/media/procguid_4.png "ProcGuid_4")|Child nodes of a method have data only for the calls of the parent method. When **Show External Code** is disabled, app methods can also contain an **[External Code]** node.|
162162

163+
For help understanding unexpected data in the call tree, see [Understanding the call tree](../profiling/understand-call-tree-data.md).
164+
163165
#### <a name="BKMK_External_Code"></a> External code
164166

165167
::: moniker range=">=vs-2022"

docs/profiling/instrumentation.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ Here's more information on the column values:
111111

112112
- **Module** The name of the module containing the function.
113113

114+
For help understanding unexpected data in the call tree, see [Understanding the call tree](../profiling/understand-call-tree-data.md).
115+
114116
### Async calls in the Instrumentation call tree (.NET)
115117

116118
Starting in Visual Studio 2022 version 17.13 Preview 2, views representing a visual call tree nest .NET asynchronous calls under the functions where the asynchronous call was made, making it easier to trace the execution flow within a single, unified stack trace. This can help you quickly identify performance bottlenecks.
46.2 KB
Loading
12.7 KB
Loading

docs/profiling/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
href: cpu-usage.md
3737
- name: Flame Graph
3838
href: flame-graph.md
39+
- name: Understanding the call tree
40+
href: understand-call-tree-data.md
3941
- name: CPU Insights
4042
items:
4143
- name: CPU Insights
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
title: Understanding the call tree
3+
description: Learn how to interpret call tree data in the CPU Usage and Instrumentation.
4+
ms.date: 02/19/2025
5+
ms.topic: how-to
6+
author: mikejo5000
7+
ms.author: mikejo
8+
manager: mijacobs
9+
ms.subservice: debug-diagnostics
10+
---
11+
12+
# Understanding the call tree
13+
14+
The profiling tools for CPU Usage and Instrumentation in Visual Studio include a call tree view that provides a visualization of the call paths in your application, along with performance data.
15+
16+
Sometimes, the call paths that appear in the **Call Tree** view may look different than you expect. To interpret the data you see in the call tree, it helps to understand the common reasons for these differences when they occur.
17+
18+
Here are the most common reasons:
19+
20+
- Release build optimizations. Release builds perform many optimizations such as inline function calls. Inline functions don't appear in the call tree. In some cases, release build optimizations may also generate unexpected code that appears in the call tree.
21+
22+
- Asynchronous functions. Asynchronous functions execute on their own thread independent of the call path, and they normally appear in a separate node.
23+
24+
::: moniker range=">=vs-2022"
25+
For Instrumentation, you can [configure options to view .NET async calls](../profiling/instrumentation.md#async-calls-in-the-instrumentation-call-tree-net) in a more intuitive way, within the call path where the async call was made.
26+
::: moniker-end
27+
28+
- Sampling of fast functions. For sampling (CPU Usage only), functions that execute very quickly may not get sampled, in which case these functions don't appear in the call tree.
29+
30+
- Compiler generated code. Some operations generate code that isn’t intuitive. For example, async/await patterns generate state machines. Other examples include getters/setters, pattern matching, event handlers, query syntax for LINQ, source generators (for example, creation of source generators for json serialization), and other code features. In these scenarios, some generated code may appear in the call tree.
31+
32+
- Dynamically generated code. Unlike compiler-generated code, dynamically generated code is compiled on the fly. This isn't as common as compiler-generated code. The following code uses an expression tree and results in dynamically generated code.
33+
34+
![Screenshot that shows expression tree code.](media/vs-2022/call-tree-dynamic-code-expression-tree-example.png)
35+
36+
The **Call Tree** view shows that this work is happening in a generated method with a generated name, which might be unexpected.
37+
38+
:::image type="content" source="./media/vs-2022/call-tree-dynamic-code-expression-tree-visual.png" alt-text="Screenshot that shows expression tree in the Call Tree view." lightbox="media/vs-2022/call-tree-dynamic-code-expression-tree-visual.png":::
39+
40+
- Code that defers execution. For code that defers execution, such as LINQ, the call path can appear in unexpected ways if you're unfamiliar with how LINQ works. For example, if you use the following code:
41+
42+
```csharp
43+
// LINQ query to get all items less than 1,000,000
44+
var query = list.Where(number => number < 1_000_000);
45+
var count = query.Count();
46+
```
47+
48+
You may expect the call tree to show too much time spent in the `Where` statement, but the enumeration actually happens in `Count`, so `Count` may show up as a slower function in the call tree. The following example is the opposite:
49+
50+
```csharp
51+
// LINQ query to get all items less than 1,000,000
52+
var query = list.Where(number => number < 1_000_000).ToList();
53+
var count = query.Count();
54+
```
55+
56+
In this example, `ToList` forces the enumeration before `Count`, so the `Count` call is optimized and runs very fast. Instead, the `Where` statement takes the most time.
57+

0 commit comments

Comments
 (0)