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: docs/profiling/optimize-code-using-profiling-tools.md
+15-15Lines changed: 15 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,7 +16,7 @@ monikerRange: '>= vs-2022'
16
16
---
17
17
# Beginner's guide to optimizing code and reducing compute costs (C#, Visual Basic, C++, F#)
18
18
19
-
In this guide, we aim to provide a comprehensive understanding of how to optimize code to reduce compute costs effectively and save money. By leveraging Visual Studio's profiling tools, developers can gain insights into their application's performance, identify bottlenecks, and implement optimizations to improve efficiency. The case study focuses on a .NET application that performs numerous queries against a database, highlighting common performance issues and demonstrating how to use tools like the CPU Usage tool, the .NET Object Allocation tool, and the Database tool to diagnose and resolve these issues.
19
+
In this guide, we aim to provide a comprehensive understanding of how to optimize code to reduce compute costs effectively and save money. By leveraging Visual Studio's profiling tools, developers can gain insights into their application's performance, identify bottlenecks, and implement optimizations to improve efficiency.
20
20
21
21
Our goal is to equip developers with the knowledge to:
22
22
@@ -29,23 +29,23 @@ By the end of this guide, readers should be able to apply these techniques to th
29
29
30
30
## Summary
31
31
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 based on the [Entity Framework sample](/ef/core/querying/), but modified to run a large number of queries against a SQLite local database.
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 based on the [Entity Framework sample](/ef/core/querying/), but modified to run a large number of queries.
33
33
34
34
The primary performance issue with the sample application lies in how it manages compute resources and interacts with the database. The application suffers from several common performance bottlenecks that can significantly impact its efficiency and, consequently, the compute costs associated with running it. These problems include:
35
35
36
36
1.**High CPU Usage**: The application may be performing inefficient computations or processing tasks in a way that unnecessarily consumes a large amount of CPU resources. This can lead to slow response times and increased operational costs.
37
37
38
-
2.**Inefficient Memory Allocation**: .NET applications, especially those that make extensive use of the Entity Framework, can sometimes face issues related to memory usage and allocation. Inefficient memory management can lead to increased garbage collection, which in turn can affect application performance.
38
+
2.**Inefficient Memory Allocation**: Applications can sometimes face issues related to memory usage and allocation. Inefficient memory management can lead to increased garbage collection, which in turn can affect application performance.
39
39
40
40
3.**Database Interaction Overheads**: Given that the application executes a large number of queries against a database, it is prone to experiencing bottlenecks related to database interactions. This includes inefficient queries, excessive database calls, and poor use of Entity Framework capabilities, all of which can degrade performance.
41
41
42
42
The case study aims to address these issues by employing Visual Studio's profiling tools to analyze the application's performance. By understanding where and how the application's performance can be improved, developers can implement optimizations to reduce CPU usage, improve memory allocation efficiency, streamline database interactions, and optimize resource utilization. The ultimate goal is to enhance the application's overall performance, making it more efficient and cost-effective to run.
43
43
44
44
## Challenge
45
45
46
-
Addressing the performance issues in the sample .NET application presents several challenges. These challenges stem from the complexity of diagnosing performance bottlenecks, the intricacies of optimizing code without compromising functionality, and the need to balance improvements across different aspects of the application. The key challenges in fixing the problems described are as follows:
46
+
Addressing the performance issues in the sample .NET application presents several challenges. These challenges stem from the complexity of diagnosing performance bottlenecks. The key challenges in fixing the problems described are as follows:
47
47
48
-
1.**Diagnosing Performance Bottlenecks**: One of the primary challenges is accurately identifying the root causes of the performance issues. High CPU usage, inefficient memory allocation, and database interaction overheads can have multiple contributing factors. Developers must use profiling tools effectively to diagnose these issues, which requires a deep understanding of how these tools work and how to interpret their output.
48
+
1.**Diagnosing Performance Bottlenecks**: One of the primary challenges is accurately identifying the root causes of the performance issues. High CPU usage, inefficient memory allocation, and database interaction overheads can have multiple contributing factors. Developers must use profiling tools effectively to diagnose these issues, which requires a good understanding of how these tools work and how to interpret their output.
49
49
50
50
2.**Optimizing Database Interactions**: The application's heavy reliance on database queries means that optimizing these interactions is crucial. However, doing so without a thorough understanding of the Entity Framework's inner workings and best practices can be challenging. Developers must identify inefficient queries and excessive database calls, then refactor them without affecting the application's functionality or data integrity.
51
51
@@ -83,20 +83,20 @@ In this view, we see the hot path again, which shows high CPU usage for the `Get
83
83
84
84
:::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":::
85
85
86
-
To get a visualized call tree and a different view of the data, 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.
86
+
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.
87
87
88
88
:::image type="content" source="./media/optimize-code-cpu-usage-flame-graph.png" alt-text="Screenshot of Flame Graph view in the CPU Usage tool.":::
89
89
90
90
### Gather additional data
91
91
92
-
Often, other tools can provide additional information to help the analysis and isolate the problem. For this example, take the following approach:
92
+
Often, other tools can provide additional information to help the analysis and isolate the problem. For this example, we take the following approach:
93
93
94
-
- First, 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.
94
+
- 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.
95
95
- Because we identified the LINQ DLLs, we'll also look at the Database tool.
96
96
97
97
#### Check the memory usage
98
98
99
-
To see what's going on with the app in terms of memory usage, collect a trace using the .NET Object Allocation tool (For C++, use the Memory Usage tool instead). The **Call Tree** view in the memory trace shows the hot path and helps us identify an area of high memory usage. No surprise at this point, the `GetBlogTitleX` method appears to be generating a lot of objects! Over 900,000 object allocations, in fact.
99
+
To see what's going on with the app in terms of memory usage, we collect a trace using the .NET Object Allocation tool (For C++, you can use the Memory Usage tool instead). The **Call Tree** view in the memory trace shows the hot path and helps us identify an area of high memory usage. No surprise at this point, the `GetBlogTitleX` method appears to be generating a lot of objects! Over 900,000 object allocations, in fact.
100
100
101
101
:::image type="content" source="./media/optimize-code-dotnet-object-allocations.png" alt-text="Screenshot of Call Tree view in the .NET Object Allocation tool.":::
102
102
@@ -119,7 +119,7 @@ Notice that we are retrieving a lot of column values here, perhaps more than we
119
119
120
120
### Optimize code
121
121
122
-
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.
122
+
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.
@@ -150,23 +150,23 @@ In this code, we made several changes to help optimize the query:
150
150
- Added the `Where` clause and eliminate one of the `foreach` loops.
151
151
- Projected only the Title property in the `Select` statement, which is all we need in this example.
152
152
153
-
Next, retest using the profiling tools.
153
+
Next, we retest using the profiling tools.
154
154
155
155
## Results
156
156
157
-
After updating the code, re-run the CPU Usage tool to collect a trace. The **Call Tree** view shows that `GetBlogTitleX` is running only 1754 ms, using 37% of the app's CPU total, a significant improvement from 59%.
157
+
After updating the code, we re-run the CPU Usage tool to collect a trace. The **Call Tree** view shows that `GetBlogTitleX` is running only 1754 ms, using 37% of the app's CPU total, a significant improvement from 59%.
158
158
159
159
:::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.":::
160
160
161
-
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.
161
+
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.
162
162
163
163
:::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.":::
164
164
165
-
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.
165
+
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.
166
166
167
167
:::image type="content" source="./media/optimize-code-database-fixed.png" alt-text="Screenshot of faster query time in the Database tool.":::
168
168
169
-
Next, recheck the results in the .NET Object Allocation tool, and see that `GetBlogTitleX` is only responsible for 56,000 object allocations, nearly a 95% reduction from 900,000!
169
+
Next, we recheck the results in the .NET Object Allocation tool, and see that `GetBlogTitleX` is only responsible for 56,000 object allocations, nearly a 95% reduction from 900,000!
170
170
171
171
:::image type="content" source="./media/optimize-code-dotnet-object-allocations-fixed.png" alt-text="Screenshot of reduced memory allocations in the .NET Object Allocation tool.":::
0 commit comments