Simulating Performance Improvements with dotTrace
Let’s say you’re trying to improve performance of some application that is doing poorly. You manage to find what could potentially be a bottleneck and decide that it needs fixing. Of course, Murphy’s Law dictates that the potential issue you’ve found is probably one of the harder problems to solve as it might involve improving a calculation algorithm or changing some third party framework.
This could turn into quite an expensive operation. It would be a shame to spend time and resources on it to not have a the desired impact on the overall system. In other words, it’s best to know you’re barking up the right tree.
That’s where dotTrace Adjust Time feature comes in very handy. By allowing us to adjust the amount of time spent in a particular spot of an application, we can simulate the overall impact it would if we were to perform optimizations in that area, without having to actually do them first.
Let’s take a look at the following call tree of a performance trace for a sample application
Looking at the call trace, we can see that the majority of the time spent is dedicated to IntersectRay. This method in turn calls Sphere.Intersect and Plane.Intersect. Out of the two, the first is the more time-consuming
What would happen if we were to now try and optimize the performance of Sphere.Intersect? What overall impact would it have if the time it takes to complete the call were reduced by 30%.
Now, instead of spending time on re-designing the method, we will use the simulation functionality in dotTrace to verify whether it’s worth a potential effort.
To simulate the improvement, we right-click on the Intersect line and select Adjust Time
We are then prompted by a dialog box that allows us to adjust the time by a factor of 0 to 100.
Apart from adjusting the time, we can indicate whether we want the adjustment to be only on the specified call stack that we are currently on (Selected Call Stack), applied it to all call stacks (All call stacks) of this method or call stacks with the calling method as the parent function (this last option is available in dotTrace 4.5 EAP only).
We can also specify whether we want the adjustment to effect the overall time of the function (the function itself and all functions it calls), only the function’s own time or all functions of the the specified class.
It is important when applying adjustments to take these options into account because selecting different values can have an impact not only on the current call stack we are trying to optimize but also on the entire application.
In our case, we are going to apply adjustments to all call stacks and to the function’s total time. We will apply a 30% reduction, which means we will enter 70 as the Factor value. This now produces the following result
First thing to notice is that the line where we’ve adjusted the time is crossed out. Next to each time, we now see a new amount in the color green and the original time in blue in brackets. Focusing on the IntersectRay method, a 30% improvement of Sphere.Intersect would lead to approximately 19% improvement on the call and an overall increase of 15% in terms of performance. Is that worth it?
Let’s dig deeper. Opening up Sphere.Intersect we see that 24% of the time is dedicated to the Vector.op_Multiply. What would happen if we were to focus our improvement exclusively on this method call? Applying the same 30% increase in performance, we’d end up with an overall increase of 14%
Despite representing only 1/3 of the total time of Sphere.Intersect, an improvement on that call alone will provide us with the same overall performance increase as concentrating on the complete method. In fact, it makes more sense to optimize the op_Multiply method since it’s used extensively throughout the application. That’s where we should focus our efforts!
Optimization Shortcuts in 4.5
dotTrace 4.5, which at the time of publishing this post is in EAP, introduces some shortcuts for time adjustment. When right-clicking on Adjust Time context menu, a submenu displays two additional options:
which allow us to quickly optimize the current (or all) instances of a specific method. Optimizing means setting the total time to 0.
Experimenting before committing
The reason for the Adjust Time feature in dotTrace is for experimenting with different calls before actually having to commit to changes. Often a 10% increase in one function can lead to a 20% overall improvement. Other times, it can lead to a 2% improvement. Knowing this before hand is very valuable since it provides us with something to balance against to see not only whether we’re barking up the wrong tree, but also if it’s worth barking at all.