While working on my most recent pull request to Fungus, I was told that Linq and foreach really shouldn’t be used with Unity because it can cause significant slowdowns. Noooooo! So I came up with an idea of what I wanted to implement based off of some provided reading(1)(2) but I wanted to SEE the change in speed for myself. So I decided to use Unity’s built-in Profiler.
To open the Profiler, go to Window->Profiler or hit Ctrl+7.
You can run the profiler and get a broad overview of how much your game hogs resources. But to test a specific method or chunk of code, you can create a custom profiler sample like I did here on my potentially bad bit of code:
1 2 3 4 5 |
Profiler.BeginSample("CharacterSearch"); if (!string.IsNullOrEmpty(characterKey)) character = CharacterSearch(characterKey); Profiler.EndSample(); |
I ran the profiler and I watched as the graph went up and down, not much of it making that much sense. Then I saw a spike in the graph where I was sure the problem bit of code ran. I entered the name of my sample, which was “CharacterSearch”, into the search bar to make it easier to find, then I navigated back through the frames until I saw it pop up in the hierarchy view. And that’s where I found this:
Garbage Collection Allocation of 0.7kb and time of 2.28ms. It might seem small but this stuff adds up, especially if you want to port to mobile. Let’s see if I can do better!
So I changed my code around to use a struct with a custom compare.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
protected struct CharacterItem { public string name; public string nameText; public Character character; public bool CharacterMatch(string matchString) { return name.StartsWith(matchString, true, System.Globalization.CultureInfo.CurrentCulture) || nameText.StartsWith(matchString, true, System.Globalization.CultureInfo.CurrentCulture); } } |
…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
if (!string.IsNullOrEmpty(characterKey)) { Profiler.maxNumberOfSamplesPerFrame = -1; //You might not need this Profiler.BeginSample("CharacterSearch"); for (int i = 0; i < characters.Length; i++) { if( characters[i].CharacterMatch(characterKey)) { character = characters[i].character; } } Profiler.EndSample(); } |
When I ran the profiler again with the new optimized code, I got much better results:
0B GC Alloc and .34 ms! Wow…I really didn’t expect that much of a change.
Let me know if this is totally wrong or if there’s anything else I could do to speed this up in the comments. 🙂