Memory leaks and Utilization of GPU memory are the top causes of performance problems in most WPF applications. They are easy to have but can be difficult to find. Sometimes it can be very difficult to identify what exactly is causing the issue. Using an application performance profiler helps identify the source of these performance bottlenecks in your code base.
We faced memory leakage issues in our WPF application which we attributed mainly to the bitmaps used. The app’s primary goal was to scan number-plates of vehicles parked by the roadside using a live stream from a Camera mounted atop a moving vehicle and decipher their plate numbers using an ANPR engine. The continuous video stream from the live camera was crashing the application every few hours due to some memory leakage that we could not fix for a long time. The Graphics Processing Unit (GPU) after reaching a threshold limit would cause the application to crash.
When we started debugging the issues of memory leakage and traced it through GPU-Monitoring tools and Profilers, we found that the frames in the bitmap, extracted from a live camera, were not getting garbage collected as expected in the .NET Framework. We came across many blogs explaining different ways to clean-up bitmap memory but even after trying all possible suggestions we did not see any improvement.
Then we came across a suggestion that explained the limitation of GPU memory utilization on a 32-bit machine. Taking the clue, we correlated the issue and found that while our application targeted 64-bit, it did use a few 32-bit components. We converted and replaced every third-party component used by the application into 64-bit compatible ones. We had already optimized code to dispose bitmap images and memory stream objects. The result was a major improvement in performance and the memory leakage issues were thus resolved.
And though the solution seemed simple I can tell you it was not. The steps we went through to reach the end were many. And we learned a lot on the way. Here I share with you only a few of the tools we used to monitor and debug the performance of the application. These tools surely proved very helpful to us…
 GPU-monitoring tool:
Windows 10’s Task Manager has detailed GPU-monitoring tools that enabled us to view application-wide and system-wide GPU usage. Windows uses newly added features in the Windows Display Driver Model to pull this information directly from the GPU scheduler and video memory manager which are responsible for actually allocating the resources and claims to show very accurate data no matter which API application we have used to access the GPU.
In the full view of Task Manager, on the “Processes” tab, right-click any column header, and then enable the “GPU” and “GPU Engine” option. This adds two columns that let you see the percentage of GPU resources each application is using and which GPU engine an application is using.
We were further curious about how much video memory our application was using. To see this, we switched over to the Details tab in the Task Manager. We enabled the “GPU,” “GPU Engine,” “Dedicated GPU Memory,” and “Shared GPU Memory” columns. The “Dedicated GPU Memory” column showed us how much memory the application was using on our GPU. The “Shared GPU Memory” column showed how much memory the application was currently using for video features out of the computer’s normal system RAM.
To monitor overall GPU resource usage statistics, we can click the “Performance” tab and look for the “GPU” option in the sidebar. If our computer has multiple GPUs, it will show multiple GPU options here. Windows displays real-time GPU usage here.
 Visual Studio Diagnostic Tools:
The Diagnostic Tools window is often the preferred way to profile apps. While debugging, we could use the Diagnostic Tools window to analyse CPU and memory usage and view the events showing performance-related information.
CPU Usage: From the Summary View of the Diagnostic Tools, we Enabled CPU Profiling. To use the tool most effectively, we set two breakpoints in our code, one at the beginning and one at the end of the region of code we wanted to analyse. Examining the profiling data at the second breakpoint, the CPU Usage view showed us a list of functions ordered by the longest running. This helped us detect the probable functions that could be performance bottlenecks.
Memory Usage: The Diagnostic Tools window also allows us to evaluate memory usage in our app.
To analyse memory usage, we took two memory snapshots while debugging the application… one right before the suspected memory issue and the second right after the suspected memory issue occurred. Then with a diff of the two snapshots, we could see the exact change in the number of objects and the heap size.
 Visual Studio Performance Profiler:
In the above discussion, the diagnostic tools were being used in Debug mode. But for Release builds, we can run profiling tools post-mortem using the Performance Profiler which can collect diagnostic info while the app is running. We can then examine the collected information after the app is stopped.
Since ours was a WPF application, we enabled Application Timeline through the Performance Profiler. In all XAML apps, such as Windows desktop WPF apps and UWP apps, the Application Timeline tool provides a helpful analysis of resource consumption.
We studied it to see if there were low frame rates in Visual throughput graph or high numbers in UI thread utilization graph to look for possible visual problems or UI responsiveness issues in the application.
That brings us to the end of this post. Although there were many more experiments we did with the code and many more tools we used. But I have only listed the most useful and helpful ones above.>