Skip to content

Commit b90c9c3

Browse files
committed
Merged main into live
2 parents e40eff9 + 05a52d5 commit b90c9c3

7 files changed

+36
-32
lines changed

docs/profiling/index.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ landingContent:
2727
url: beginners-guide-to-performance-profiling.md
2828
- text: Get started measuring memory usage
2929
url: memory-usage.md
30+
- linkListType: how-to-guide
31+
links:
3032
- text: Beginner's guide to optimizing code
3133
url: optimize-code-using-profiling-tools.md
3234
- text: Isolate a performance issue
Loading
Loading
Loading
Loading

docs/profiling/optimize-code-using-profiling-tools.md

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,20 @@ monikerRange: '>= vs-2022'
1616
---
1717
# Beginner's guide to optimizing code and reducing compute costs (C#, Visual Basic, C++, F#)
1818

19-
Reducing your compute time means reducing costs, so optimizing your code can save money. In this case study, we show how you can use profiling tools to help you accomplish this task.
19+
Reducing your compute time means reducing costs, so optimizing your code can save money. This case study uses a sample application with performance issues to demonstrate how to use profiling tools to improve efficiency.
2020

21-
Our goal is to equip developers with the knowledge to:
21+
This case study covers these topics:
2222

23-
- Understand the importance of code optimization and its impact on reducing compute costs.
24-
- Utilize Visual Studio profiling tools to analyze application performance.
25-
- Interpret the data provided by these tools to identify performance bottlenecks.
26-
- Apply practical strategies to optimize code, focusing on CPU usage, memory allocation, and database interactions.
23+
- The importance of code optimization and its impact on reducing compute costs.
24+
- How to use Visual Studio profiling tools to analyze application performance.
25+
- How to interpret the data provided by these tools to identify performance bottlenecks.
26+
- How to apply practical strategies to optimize code, focusing on CPU usage, memory allocation, and database interactions.
2727

28-
By the end of this guide, readers should be able to apply these techniques to their own projects, leading to more efficient and cost-effective applications.
28+
Follow along and then apply these techniques to your own applications to make them more efficient and cost-effective.
2929

3030
## Optimization case study
3131

32-
The sample application discussed in this case study is a .NET application designed to run queries against a database of blogs and associated blog posts. It utilizes the Entity Framework, a popular ORM (Object-Relational Mapping) for .NET, to interact with a SQLite local database. The application is structured to execute a large number of queries, simulating a real-world scenario where a .NET application might be required to handle extensive data retrieval tasks. The sample application is a modified version of the [Entity Framework sample](/ef/core/querying/).
32+
The sample application examined in this case study is a .NET application that runs queries against a database of blogs and blog posts. It utilizes the Entity Framework, a popular ORM (Object-Relational Mapping) for .NET, to interact with a SQLite local database. The application is structured to execute a large number of queries, simulating a real-world scenario where a .NET application might be required to handle extensive data retrieval tasks. The sample application is a modified version of the [Entity Framework getting started sample](/ef/core/get-started/overview/first-app).
3333

3434
The primary performance issue with the sample application lies in how it manages compute resources and interacts with the database. The application suffers from a common performance bottleneck that significantly impacts its efficiency and, consequently, the compute costs associated with running it. The problem includes the following symptoms:
3535

@@ -53,42 +53,44 @@ Addressing these challenges requires a strategic approach that combines effectiv
5353

5454
## Strategy
5555

56-
Here is a high-level view of the approach:
56+
Here is a high-level view of the approach in this case study:
5757

5858
- We start the investigation by taking a CPU usage trace. Visual Studio's [CPU Usage tool](../profiling/cpu-usage.md) is often helpful to begin performance investigations and to optimize code to reduce cost.
5959
- Next, to get additional insights to help isolate issues or improve the performance, we collect a trace using one of the other profiling tools. For example:
6060
- We take a look at the memory usage. For .NET, we try the [.NET Object Allocation tool](../profiling/dotnet-alloc-tool.md) first. (For either .NET or C++, you can look at the Memory Usage tool instead.)
6161
- For ADO.NET or Entity Framework, we can use the [Database tool](../profiling/analyze-database.md) to examine SQL queries, precise query time, and more.
6262

63-
Data collection requires the following steps:
63+
Data collection requires the following tasks:
6464

65-
- We set the app to a Release build.
66-
- We select the CPU Usage tool from the Performance Profiler (**Alt+F2**). (Later steps involve a few of the other tools.)
67-
- From the Performance Profiler, we start the app and collect a trace.
65+
- Setting the app to a Release build.
66+
- Selecting the CPU Usage tool from the Performance Profiler (**Alt+F2**). (Later steps involve a few of the other tools.)
67+
- From the Performance Profiler, start the app and collect a trace.
6868

6969
### Inspect areas of high CPU usage
7070

71-
After collecting a trace with the CPU Usage tool and loading it into Visual Studio, we first check the initial *.diagsession* report page that shows Top Insights and the Hot Path. The Hot Path shows the code path with highest CPU usage in the app. These sections may provide tips to help us quickly identify performance issues that we can improve.
71+
After collecting a trace with the CPU Usage tool and loading it into Visual Studio, we first check the initial *.diagsession* report page that shows summarized data. Use the **Open details** link in the report.
7272

73-
We can also view the hot path in the **Call Tree** view. To open this view, use the **Open details** link in the report and then select **Call Tree**.
73+
:::image type="content" source="./media/optimize-code-open-details.png" alt-text="Screenshot of opening details in CPU Usage tool.":::
7474

75-
In this view, we see the hot path again, which shows high CPU usage for the `GetBlogTitleX` method in the app, using about a 60% share of the app's CPU usage. However, the **Self CPU** value for `GetBlogTitleX` is low, only about .10%. Unlike **Total CPU**, the **Self CPU** value excludes time spent in other functions, so we know to look farther down the Call Tree view for the real bottleneck.
75+
In the report details view, open the **Call Tree** view. The code path with highest CPU usage in the app is called the *hot path*. The hot path flame icon (![Screenshot that shows Hot Path icon.](../profiling/media/optimize-code-hot-path-icon.png)) can help to quickly identify performance issues that might be improved.
76+
77+
In the **Call Tree** view, you can see high CPU usage for the `GetBlogTitleX` method in the app, using about a 60% share of the app's CPU usage. However, the **Self CPU** value for `GetBlogTitleX` is low, only about .10%. Unlike **Total CPU**, the **Self CPU** value excludes time spent in other functions, so we know to look farther down the call tree for the real bottleneck.
7678

7779
:::image type="content" source="./media/optimize-code-cpu-usage-call-tree.png" alt-text="Screenshot of Call Tree view in the CPU Usage tool.":::
7880

79-
`GetBlogTitleX` makes external calls to two LINQ DLLs, which are using most of the CPU time, as evidenced by the very high **Self CPU** values. This is the first clue that we may want to look for a LINQ query as an area to optimize.
81+
`GetBlogTitleX` makes external calls to two LINQ DLLs, which are using most of the CPU time, as evidenced by the very high **Self CPU** values. This is the first clue that a LINQ query might be an area to optimize.
8082

8183
:::image type="content" source="./media/optimize-code-cpu-usage-call-tree-self-cpu.png" alt-text="Screenshot of Call Tree view in the CPU Usage tool with Self CPU highlighted." lightbox="./media/optimize-code-cpu-usage-call-tree-self-cpu.png":::
8284

83-
To get a visualized call tree and a different view of the data, we right-click `GetBlogTitleX` and choose **View in Flame Graph**. Here again, it looks like the `GetBlogTitleX` method is responsible for a lot of the app's CPU usage (shown in yellow). External calls to the LINQ DLLs show up beneath the `GetBlogTitleX` box, and they are using all of the CPU time for the method.
85+
To get a visualized call tree and a different view of the data, open the **Flame Graph** view. (Or, right-click `GetBlogTitleX` and choose **View in Flame Graph**.) Here again, it looks like the `GetBlogTitleX` method is responsible for a lot of the app's CPU usage (shown in yellow). External calls to the LINQ DLLs show up beneath the `GetBlogTitleX` box, and they're using all of the CPU time for the method.
8486

8587
:::image type="content" source="./media/optimize-code-cpu-usage-flame-graph.png" alt-text="Screenshot of Flame Graph view in the CPU Usage tool.":::
8688

8789
### Gather additional data
8890

89-
Often, other tools can provide additional information to help the analysis and isolate the problem. For this example, we take the following approach:
91+
Often, other tools can provide additional information to help the analysis and isolate the problem. In this case study, we take the following approach:
9092

91-
- First, we take a look at memory usage. There might be a correlation between high CPU usage and high memory usage, so it can be helpful to look at both to isolate the issue.
93+
- First, look at memory usage. There might be a correlation between high CPU usage and high memory usage, so it can be helpful to look at both to isolate the issue.
9294
- Because we identified the LINQ DLLs, we'll also look at the Database tool.
9395

9496
#### Check the memory usage
@@ -101,22 +103,22 @@ Most of the objects created are strings, object arrays, and Int32s. We may be ab
101103

102104
#### Check the query in the Database tool
103105

104-
In the Performance Profiler, we select the Database tool instead of CPU Usage (or, we can select both). When we've collected a trace, select the **Queries** tab in the diagnostics page. In the Queries tab for the Database trace, we can see the first row shows the longest query, 2446 ms. The **Records** column shows how many records the query reads. We can use this information for later comparison.
106+
In the Performance Profiler, we select the Database tool instead of CPU Usage (or, select both). When we've collected a trace, open the **Queries** tab in the diagnostics page. In the Queries tab for the Database trace, you can see the first row shows the longest query, 2446 ms. The **Records** column shows how many records the query reads. You can use this information for later comparison.
105107

106108
:::image type="content" source="./media/optimize-code-database.png" alt-text="Screenshot of Database queries in the Database tool.":::
107109

108-
By examining the `SELECT` statement generated by LINQ in the Query column, we identify the first row as the query associated with the `GetBlogTitleX` method. To view the full query string, we expand the column width. The full query string is:
110+
By examining the `SELECT` statement generated by LINQ in the Query column, we identify the first row as the query associated with the `GetBlogTitleX` method. To view the full query string, expand the column width. The full query string is:
109111

110112
```sql
111113
SELECT "b"."Url", "b"."BlogId", "p"."PostId", "p"."Author", "p"."BlogId", "p"."Content", "p"."Date", "p"."MetaData", "p"."Title"
112114
FROM "Blogs" AS "b" LEFT JOIN "Posts" AS "p" ON "b"."BlogId" = "p"."BlogId" ORDER BY "b"."BlogId"
113115
```
114116

115-
Notice that we are retrieving a lot of column values here, perhaps more than we need. Let's look at the source code.
117+
Notice that the app is retrieving a lot of column values here, perhaps more than we need. Let's look at the source code.
116118

117119
### Optimize code
118120

119-
It's time to take a look at the `GetBlogTitleX` source code. In the Database tool, we right-click the query and choose **Go to Source File**. In the source code for `GetBlogTitleX`, we find the following code that uses LINQ to read the database.
121+
It's time to take a look at the `GetBlogTitleX` source code. In the Database tool, right-click the query and choose **Go to Source File**. In the source code for `GetBlogTitleX`, we find the following code that uses LINQ to read the database.
120122

121123
```csharp
122124
foreach (var blog in db.Blogs.Select(b => new { b.Url, b.Posts }).ToList())
@@ -131,7 +133,7 @@ foreach (var blog in db.Blogs.Select(b => new { b.Url, b.Posts }).ToList())
131133
}
132134
```
133135

134-
This code uses `foreach` loops to search the database for any blogs with "Fred Smith" as the author. Looking at it, we can see that a lot of objects are getting generated in memory: a new object array for each blog in the database, associated strings for each URL, and values for properties contained in the posts, such as blog ID.
136+
This code uses `foreach` loops to search the database for any blogs with "Fred Smith" as the author. Looking at it, you can see that a lot of objects are getting generated in memory: a new object array for each blog in the database, associated strings for each URL, and values for properties contained in the posts, such as blog ID.
135137

136138
We do a little research and find some common recommendations for how to optimize LINQ queries and come up with this code.
137139

@@ -155,11 +157,11 @@ After updating the code, we re-run the CPU Usage tool to collect a trace. The **
155157

156158
:::image type="content" source="./media/optimize-code-cpu-usage-call-tree-fixed.png" alt-text="Screenshot of improved CPU usage in the Call Tree view of the CPU Usage tool.":::
157159

158-
We switch to the **Flame Graph** view to see another visualization of the improvement. In this view, `GetBlogTitleX` also uses a smaller portion of the CPU.
160+
Switch to the **Flame Graph** view to see another visualization showing the improvement. In this view, `GetBlogTitleX` also uses a smaller portion of the CPU.
159161

160162
:::image type="content" source="./media/optimize-code-cpu-usage-flame-graph-fixed.png" alt-text="Screenshot of improved CPU usage in the Flame Graph view of the CPU Usage tool.":::
161163

162-
We check the results in the Database tool trace, and only two records are read using this query, instead of 100,000! Also, the query is much simplified and eliminates the unnecessary LEFT JOIN that was generated previously.
164+
Check the results in the Database tool trace, and only two records are read using this query, instead of 100,000! Also, the query is much simplified and eliminates the unnecessary LEFT JOIN that was generated previously.
163165

164166
:::image type="content" source="./media/optimize-code-database-fixed.png" alt-text="Screenshot of faster query time in the Database tool.":::
165167

@@ -169,7 +171,7 @@ Next, we recheck the results in the .NET Object Allocation tool, and see that `G
169171

170172
## Iterate
171173

172-
Multiple optimizations may be necessary and we can continue to iterate with code changes to see which changes improve performance and reduce our compute cost.
174+
Multiple optimizations may be necessary and we can continue to iterate with code changes to see which changes improve performance and help reduce the compute cost.
173175

174176
## Next steps
175177

docs/profiling/toc.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@
1010
href: choose-performance-tool.md
1111
- name: Run profiling tools with or without the debugger
1212
href: running-profiling-tools-with-or-without-the-debugger.md
13+
- name: Beginner's guide to optimizing code
14+
href: optimize-code-using-profiling-tools.md
15+
- name: Isolate a performance issue
16+
href: isolate-performance-issue.md
1317
- name: Tutorials
1418
items:
1519
- name: Measure application performance in Visual Studio
1620
href: beginners-guide-to-performance-profiling.md
1721
- name: Measure memory usage in Visual Studio
1822
href: memory-usage.md
19-
- name: Beginner's guide to optimizing code
20-
href: optimize-code-using-profiling-tools.md
21-
- name: Isolate a performance issue
22-
href: isolate-performance-issue.md
2323
- name: Concepts
2424
items:
2525
- name: Understanding performance collection methods

0 commit comments

Comments
 (0)