Unity: Comparison of performance cost between "GameObject.Find" and direct referencing
Tags: unity3d
Published: 2020-04-04 02:16:56

When I was developing Unity games, I had learned that `GameObject.Find` is slow once the size of object becomes bigger. Also, many game developers suggested against using such method, as it not only slows down the program, but also impacts the overall maintainability of the code. But I have never get a chance to quantify how *slow* will the game become if we exploit the usage of `GameObject.Find`. Therefore, it is a good time to perform an experiment on these two methods. In this article, I will do a very simple experiment on the following methods. All of these methods perform the same action: Getting a reference to a game object.

- `GameObject.Find`
- Set up the public variable in the script, then drag the other game object to the reference box in Unity Editor.

The development environment I am on is 2019.3.7.f1. I will perform the scripting in this experiment using C#. Here is how I set up the structure of the experiment:

- Independent variable: The method of accessing the reference to a `Cube` game object
- Dependent variable: The time elapsed after scanning through all the `Cube` game object using the given method
- The CPU, RAM usage, and other game objects (`Main Camera`, `Directional Light`) are controlled and won't change. 

# Getting Started

Firstly, I created a new Unity project, as usual. 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4wbfu75m1a21o16fgdmv.png)

My brand new project looked as empty as this:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xpo8xhts6d6tj8jeiwmo.png)

# Testing the performance of direct referencing

To start testing, it's time to write some scripts -- under `Assets` folder, I created  `scripts` directory and then added a new script, as you can see from the screen shot below:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sak3j2jdfj16vyij20v2.png)

Here is how the test is implemented. To test the performance of direct referencing an object in Editor, we firstly create 10,000 GameObjects using the following script: 
```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class FindTest : MonoBehaviour
{
    private static int size = 10000;
    public GameObject[] cubes = new GameObject[size];
    void OnEnable()
    {
        cubes = new GameObject[size];
        for (int i = 0; i < size; i++){
            GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
            cube.name = i.ToString();
            cubes[i] = cube;
        }
    }
}
```
To use this script, simply create an empty game object. Then, on the right side, search for the `Create` script. Once you load the `Create` script, 10,000 cubes will pop up in the editor, and they are all binded to the `cubes` array in `Create` class.
Once we finished creating these GameObjects, the editor will look like this:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/eovd19d8n23fe3qm56d2.png)

Since each cube has a `BoxCollider` component, our goal is to turn off the `BoxCollider` of all the components. We will measure how long does this action take.

We use the following script to perform the action, which is modified based upon the previous script: We removed the procedure for *creating* the cubes, and replaced them with `cubes[i].GetComponent<BoxCollider>().enabled = false`.

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Create : MonoBehaviour
{
    private static int size = 10000;
    public GameObject[] cubes;

    void OnEnable()
    {
        for (int i = 0; i < size; i++){
            cubes[i].GetComponent<BoxCollider>().enabled = false;
        }
        Debug.Log(Time.realtimeSinceStartup);
    }
}
```
Once Unity finishes the action, the **time elapsed (in seconds)** will be sent to the console. We perform the same action 10 times. The console will then look like this, if we turn off `clear on play` and all other options. The console will collect all the data once we finish the experiment. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qdt4nbjxrywh8oqx2aw6.png)

We extracted the dataset into a spreadsheet.
The following table shows the result after this first experiment:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2ukwmm4grq25a7afhgp0.png)

Now, let us move on to the second test, to see how does `GameObject.Find()` performs.
## Testing `GameObject.Find()`

Now let us test the performance of `GameObject.Find()`. We can use the testing scripts we developed previously, with some slight modifications. In fact, we only modified one line -- changing from `cubes[i].GetComponent<BoxCollider>().enabled = false;` to `GameObject.Find(i.ToString()).GetComponent<BoxCollider>().enabled = false;`. This will change the way we access to the Cube objects.

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Create : MonoBehaviour
{
    private static int size = 10000;
    public GameObject[] cubes;

    void OnEnable()
    {
        for (int i = 0; i < size; i++){
            GameObject.Find(i.ToString()).GetComponent<BoxCollider>().enabled = false; // Modified line
        }
        Debug.Log(Time.realtimeSinceStartup);
    }
}
```
After performing the test 10 times (same as before), we recorded the following data -- indeed, as we can see, `GameObject.Find()` took longer in accessing all the game objects and turn their `BoxCollider`s off.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/c7hzgrfi515ttys7aemd.png)


# Testing on a smaller number of game objects
We have seen that `GameObject.Find()` significantly lags behind when there is a large number of GameObjects in the scene. But what if we change the number of objects into a smaller one, e.g. 100? Let's do the same experiment with the smaller number. 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/m9pz3lytc3ujyih955ah.jpg)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ku6585817s3sw9en8k9z.png)

We can see from the figure above there isn't a huge time difference between these two methods if we reduce the number of game objects in the experiment -- `GameObject.Find` even has a tiny edge over direct referencing in this smaller experiment. After gathering all the data, we produced the following figure, whereas each bar represents the average time of using the corresponding method:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1of7demrcicsiqajiz1a.png)

# Conclusion 

In conclusion, we can discover that `GameObject.Find` has worse performance comparing to direct referencing in the experiment. This is potentially due to the fact that `GameObject.Find` needs to linearly find the object with corresponding name, whereas the direct referencing method does not need to perform linear search. Overall, if one doesn't *really need to* use `GameObject.Find`, it should be avoided. 

However, in this experiment, we have only explored two of the many methods for referencing a game object in Unity. There are many methods that we can also include in this experiment. For instance, we have not tried `FindObjectOfType` and `FindGameObjectsWithTag` yet. These are the methods that we can include in the future experiments.