Simple Usage of Stopwatch
We’re often told that such and such method is slow and should be avoided at all costs. Two common ones are using Vector magnitudes vs. square magnitudes and repeated calls to Unity’s GetComponent() method. Let’s start off by testing the difference between Vector3.magnitude vs. Vector3.sqrMagnitude. Place your bets now on which will be faster and what the time difference is!
First simply create a MonoBehaviour derived script with just an empty Start() method. Then pop in the following method.
The code should be self-explanatory. Basically we just set up a bunch of random Vector3s and record the time it takes to do a bunch of iterations of sqrMagnitude and magnitude. Now for the results! On my system (AMD 8350 CPU and Unity 5.3.4) the results are:
Magnitude test results:
total sqrMagnitude time: 14ms
average sqrMagnitude time: 1.4E-05ms
total magnitude time: 35ms
average magnitude time: 3.5E-05ms
So, as expected, magnitude calls are much slower than sqrMagnitude, approximately 3 times slower. However, this is 1 million iterations! In conclusion, although magnitude is definitely slower than square magnitude, you’d have to call it over 70,000 times to see an impact of 1ms. Though I’ll probably still suggest usage of square magnitude, the difference is not as big as what people make of it. Let’s check something else.
Another commonly frowned upon method call is gameObject.GetComponent<T>(). This is considered quite slow and we’re often told avoid doing this in methods that are called frequently, like Update(). But how slow is it exactly? Place the following code into your testing script to find out.
The above code is basically the same concept as the magnitude test method, but here I’m calling GetComponent. Not only do I call GetComponent, but I do a a trivial setting (enable = true) just to make the call relevant. In the comparison test I only call GetComponent once then perform the setting of enable = true on that component repeatedly. The results are as follows:
GetComponent test results:
total GetComponent time: 211ms
average GetComponent time: 0.000211ms
total single GetComponent time: 60ms
average single GetComponent time: 6E-05ms
As we would expect, calling GetComponent is indeed pretty slow. Like our comparison of sqrMagnitude and magnitude, GetComponent also seems to be about 3x slower than a single call to GetComponent . What’s a bit unexpected is how slow the loop with the single call to GetComponent is. After some more testing I found that about 50% of the time spent in the second loop is due to the conditional if(al != null). Wow! That’s sort of huge and leads to an important lesson: The results are only as good as the test. So in my test method, there is a flaw that skews the results. However, I usually check if a GameObject or component is null before accessing it to prevent those lovely null reference exception errors. Maybe I should be doing try-catches instead? It’s another test for another day. In conclusion: GetComponent is pretty slow, don’t use it in Update methods and cache it whenever possible. How slow? It’ll take about 17,000 calls per frame to see a 1ms impact. But when we’re trying to get over 60fps we only get 16ms to spend per frame, so beware!
As you can see, Stopwatch is extremely useful in testing out your ideas on how to optimize your code. However, imagine the pain of setting this up for 15 methods over 100 classes. Ouch! So let’s explore a simpler and more valuable class for diagnostics.
Last: 17 Min: 14 Max: 17 Avg: 14.8 Total: 74
Last: 36 Min: 36 Max: 37 Avg: 36.2 Total: 181
Last: 196 Min: 190 Max: 196 Avg: 192.4 Total: 962
Last: 27 Min: 26 Max: 27 Avg: 26.8 Total: 134
As you can see they are mostly the same as the original tests, with some variance of course. But so much less code setup to run the timers and get the results.
Your challenge, should you accept it, is to show us some results for method calls that are commonly frowned upon. Are for loops actually faster than for each? Are string comparisons slower than int comparisons? Try out your ideas for optimizing your code and share what you find out.
As always, thanks for reading!
Stopwatch image courtesy of Designed by Freepik