This is the second part of my walkthrough of using the profiler in Visual Studio Team System 2005 to analyze a performance issue. In the first part of my walkthrough, we used the summary view to highlight two possible performance hotspots in our application. The application that we are profiling can be downloaded from GotDotNet if you want to follow along with the analysis steps. The first performance issue was that the Rational.reduce() function was taking the majority of the program’s time. Also, we noticed that Array.GetUpperBound was being called around 85,000 times, much more then any other function in the program. In this week’s walkthrough, we will examine how three other views in the IDE can help up dig up more information on these performance hotspots.
The different views can all be accessed by the buttons at the bottom of an open VSP file. After summary view, which we discussed last walkthrough, the next analysis view is the function view. The function view contains a list of all the functions that were run (in instrumentation mode) or all the functions that we encountered at any point (in sampling mode). In the columns next to each function there are several columns of aggregate data for that specific function. Below I have a screenshot of function view in instrumentation mode, sorted descending by elapsed exclusive time. As mentioned in the last walkthrough, elapsed exclusive time is the time in this function and not in any sub-functions. The default columns provide a good set of columns to diagnose performance issues, especially elapsed exclusive time. But we do provide a much larger selection of columns, just right click on any column and select “Add / Remove columns.” The extra columns can get you some interesting information like process name, line numbers, maximum or minimum exclusive time values and many others. Function view can be used in much the same fashion as summary view, but it shows you all functions, not just the top three functions in each category. Also, if you only want to examine specific modules, the function view allows you to group the function by module, just right-click on any function and select “Group by Module.”
Looking at function view doesn’t tell us much more then we already knew from summary view in this example. The two main issues mentioned above (Rational.reduce and Array.GetUpperBound) are still the two big performance villains. Perhaps it would help our analysis to see where the two trouble functions fit into the overall structure of the program. For this task, we will turn to the next two views, caller/callee view and calltree view.
Function view looks at aggregate data for a function over the programs entire run, while caller/callee view focuses on the functions that call a specific function (callers) and on the functions that the specific function calls (callees). Since we want to focus on our hotspots, right click on the Rational.reduce function in function view, and select “Show in caller/callee view.” Selecting this jumps you to the caller/callee view, with Rational.reduce selected as the current function, as shown in the screenshot below.
The caller/callee view is divided into three panes. The top pane contains all the callers. The middle pane is the current function. Finally (and perhaps obviously), the bottom pane contains all the callees. In function view, the columns total all the data for the functions, but in caller/callee the columns contain only the data for the specific current function. So for the function Prime.IsSmallPrime in the bottom pane, the value of 14997 in number of calls is the number of times IsSmallPrime was called from Rational.reduce, not the total number of times it was called. Double-clicking a function in the top or bottom pane will select that as the new current function, so you can trace a specific execution path up or down the calltree. Looking at the caller/callee view for Rational.reduce we see that Array.GetUpperBound is always being called from Rational.reduce. Perhaps our performance issue is being caused by Array.GetUpperBound being called too many times from Rational.reduce?
The calltree view provides an alternative to caller/callee for viewing profiling data along with program structure. In calltree view, the functions are placed in a tree structure corresponding to how they were called. Pictured below is the calltree for our example, with the nodes expanded to show the Rational.reduce and the Array.GetUpperBound function. Just as in caller/callee we can see that GetUpperBound is being called many times, and always from Rational.reduce. The call tree provides an easy way to browse the structure of a program, using the common UI paradigm of expanding tree nodes.
So now we think that we have isolated a possible performance issue, that being the number of times Array.GetUpperBound is called from the Rational.reduce function. Next walkthrough, we’ll look at fixing any issues in the code, then running a new performance session to see if we improved our performance. After all, the profiling data is only useful if you can use it to make your program perform better and compare it to how it used to run.