Objective:
The main Objective of this blog post is to explain how to Avoid Usage of ForEach Loop in Unity
This blog post is part of our ongoing Latest Optimization Initiative for Unity Games.
If you would like to learn how to optimize your games, you can check out this blog post:
Are you noticing some glitches in the gameplay?
Does glitches/ Lag appear while iterating over loops?
Do you have to iterate through list of many GameObjects?
If you have any of the concerns like these, then you have come to the right place!
"Usually Glitches/ LAG during game can occur due to high Garbage Collection in a single frame. So before going ahead let us first understand, What Is Garbage Collection (GC)?"
What is Garbage Collection?
- Garbage collection is an important part of memory management system of any computing device. Its main aim is to attempt to reclaim or free memory occupied by objects that are no longer in use by a program.
- It is an effective automated system which tries to make sure that unwanted objects do not keep the space occupied, and allow effective and optimized usage of memory. Though it is an automated system, programmers do have some kind of control over it.
- Usually GC is executed whenever processing power is available and on assurance that the object is no longer going to be used.
SO WHAT DOES THIS HAVE TO DO WITH FOREACH LOOP IN UNITY?
- To understand this let us take an example
Step 1 Set up scene in Unity as follow
Take an canvas and take text as shown above.
Take an empty gameobject and rename it to GameObjectList.
Create few other empty gameobjects (about 10-30 would be fine) as its children.
Step 2 Create a Script and name it as per your wish
I have named it as ForEachLoopTest.cs
I use C# as my coding preference, you can use javascript if you wish
public class ForEachLoopTest : MonoBehaviour
{
#region PUBLIC_DECLARATIONS
public List<GameObject> emptyGameObjects;
public Text indicatorText;
#endregion
#region UNITY_CALLBACKS
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
indicatorText.text = "EXECUTION ON";
}
if (Input.GetKeyUp(KeyCode.Space))
{
indicatorText.text = "HOLD SPACE TO TURN ON EXECUTION";
}
if (Input.GetKey(KeyCode.Space))
{
UpdateTextValue();
}
}
#endregion
#region PUBLIC_METHODS
public void UpdateTextValue()
{
foreach (var item in emptyGameObjects)
{
// PROCESS ITEMS IN LIST
}
// for (int i = 0; i < emptyGameObjects .Count; i++)
// {
// PROCESS ITEMS IN LIST
// }
}
#endregion
}
Note
I have commented for loop code and kept only the forEach Loop active
Step 3 Assign References and Test the Code
To Assign References and Test the Code follow the given steps:
- Attach script to GameObjectList and assign the all empty gameobjects to emptyGameObjects list
- Now execute the game.
- Open Profiler Window.
- Now Press Space button.
Do you notice something in the profiler?
Check this selected line in the profiler. It shows 40B GC Allocation on every update when the space button is pressed.
Now Stop the game and go back to the script. Comment out the foreach loop part and uncomment for loop part.
As you can see from the code the iteration of the loop will be the same in both the case
Now save and execute the game again and perform the above steps again.
Turn on the profiler and press space button.
Do you notice any change?
NO GC? Magic? Did GC have any other effect on the demo?
HOW DID IT AFFECT MY GAME?
- You might be shouting... “HEY WHAT A WASTE OF TIME, I don’t see any change except a number change in the profiler!!”
- Well in this case you are right, but now imagine there is a case where you have to run through different loops with over 1000’s of iterations, each loop giving out few bytes of GC!
- This will surely create a glitch in a mobile device with slow processor as memory allocation does take a lot of processing power, especially garbage collection.
- Now if you don’t take care and continue looping each frame, you will surely end up with not so nice game and destroy your user experience.
- So avoiding for-each loop would be a wise choice whenever possible.
OKAY MAYBE YOU ARE RIGHT, BUT WHAT IS THE REASON BEHIND THIS GC?
- So you must be wondering, it’s just a loop! What Garbage (Object) is there to collect from here?
Let us see the syntax first:
foreach (SomeType s in someList)
s.DoSomething();
Now on compile , complier will preprocess the code into:
using (SomeType.Enumerator enumerator = this.someList.GetEnumerator())
{
while (enumerator.MoveNext())
{
SomeType s = (SomeType)enumerator.Current;
s.DoSomething();
}
}
WOW! So well, in short here for-each will create an enumerator object on each iteration, the use of those objects die out when iteration gets completed.
Garbage collector will pounce on first chance it will get to destroy this useless object. Hence resulting in GC and in turn causing the lag/Glitch during game update, irritating the player.
Note
Amount of GC will differ depending on type of collection you are iterating through. In our case it’s a list and shows about 40 Bytes, but this may have been different if I would have used Dictionary or any other such collection.
OH I GET IT NOW!
I hope this is the feeling in your mind on reading this blog post! If you still don’t get it, you can always ask me questions in the comment section. I will surely get back to you for the same. So now conclusion is simple, avoid for-each loop as much as possible in your games. Each small optimization helps in the long run.
"So that’s it for this post! Keep checking our BLOG section. We will keep updating new tips and tricks of optimization. And help you simplify the hard phase of optimization."
This blog post is part of our ongoing Latest Optimization Initiative for Unity Games.
If you would like to learn how to optimize your games, you can check out this blog post:
Got an Idea of Game Development? What are you still waiting for? Contact us now and see the Idea live soon. Our company has been named as one of the best Unity 3D Game Development Company in India.
Talented Game Developer as well as a player with a mania for video games and the game industry. Always Ready to take up challenging Projects. Proud to be making with the TheAppGuruz Team