FindObjectsByTypeってどんぐらい早いの？

# 経緯
https://www.youtube.com/watch?v=QJcU7jO5HZ8&ab_channel=UnityJapan
UnityJapanで`FindObjectsOfType`よりも`FindObjectsByType`の方が早いと紹介されていた。

どうやら"Of"の方では常にInstance IDでソートされていたらしい。
`FindObjectsByType`ではそのソートを行わないことが出来るため早い。

計測結果だけ知りたい人向けにまとめたものを一番上に置いています。

# 計測結果まとめ [ms]
### FindObjectsOfType
| class\オブジェクト |50個|500個|5000個|
|:-:|:-:|:-:|:-:|
|10個|1.3|16.6|409.7|
|100個|1.7|16.7|393.0|
|500個|3.3|19.0|392.0|

### FindObjectsByType
| class\オブジェクト |50個|500個|5000個|
|:-:|:-:|:-:|:-:|
|10個|1.0|10.3|414.0|
|100個|1.3|8.7|406.3|
|500個|2.0|10.0|414.3|

クラスの数、オブジェクト数のどちらでも、増やすといずれのメソッドでも実行時間が伸びた。
ただし、クラスの数はほとんど影響しなかった。
オブジェクト数が500個の時に大きく差が出た。
オブジェクト数が5000個の時はほとんど差が出なかった。

# 感想
> オブジェクト数が500個の時に大きく差が出た。

実行時間短くなってる！やったー！

> オブジェクト数が5000個の時はほとんど差が出なかった。

なんで？？？？？
一番差が出るかと思った。
エディタで実行しているのが悪いのかと思ってビルドしてみたけど、OfとByにほぼ差が出なかった。
詳しいこと知っている人がいれば教えてください:bow:

# 計測手順
Windows 10 Home
AMD Ryzen 7 PRO 4750G
RAM 16GB
Unity 2021.3.18f1

Class数(測定用に追加するMonoBehaviourを継承したClassの数):10個, 100個, 500個
シーン上のオブジェクト数:50個, 500個, 5000個
オブジェクトにはランダムなクラスを3つずつ付与する。

FindByObjectsを100回実行した時間を測定する。

※実際のコードは記事の一番下に記載しておきます。


# 計測結果
## class10個
### GameObject50個
FindObjectsOfType
1回目 : 2ms
2回目 : 1ms
3回目 : 1ms
平均 : 1.3ms

FindObjectsByType
1回目 : 1ms
2回目 : 1ms
3回目 : 1ms
平均 : 1.0ms

### GameObject500個
FindObjectsOfType
1回目 : 17ms
2回目 : 17ms
3回目 : 16ms
平均 : 16.6ms

FindObjectsByType
1回目 : 13ms
2回目 : 9ms
3回目 : 9ms
平均 : 10.3ms

### GameObject5000個
FindObjectsOfType
1回目 : 398ms
2回目 : 392ms
3回目 : 406ms
平均 : 409.7ms

FindObjectsByType
1回目 : 411ms
2回目 : 392ms
3回目 : 439ms
平均 : 414.0ms

## class100個
### GameObject50個
FindObjectsOfType
1回目 : 3ms
2回目 : 1ms
3回目 : 1ms
平均 : 1.7ms

FindObjectsByType
1回目 : 2ms
2回目 : 1ms
3回目 : 1ms
平均 : 1.3ms

### GameObject500個
FindObjectsOfType
1回目 : 17ms
2回目 : 16ms
3回目 : 17ms
平均 : 16.7ms

FindObjectsByType
1回目 : 9ms
2回目 : 8ms
3回目 : 9ms
平均 : 8.7ms

### GameObject5000個
FindObjectsOfType
1回目 : 404ms
2回目 : 386ms
3回目 : 389ms
平均 : 393.0ms

FindObjectsByType
1回目 : 412ms
2回目 : 399ms
3回目 : 405ms
平均 : 406.3ms


## class500個
### GameObject50個
FindObjectsOfType
1回目 : 4ms
2回目 : 3ms
3回目 : 3ms
平均 : 3.3ms

FindObjectsByType
1回目 : 3ms
2回目 : 2ms
3回目 : 1ms
平均 : 2.0ms

### GameObject500個
FindObjectsOfType
1回目 : 20ms
2回目 : 18ms
3回目 : 19ms
平均 : 19.0ms

FindObjectsByType
1回目 : 10ms
2回目 : 10ms
3回目 : 10ms
平均 : 10.0ms

### GameObject5000個
FindObjectsOfType
1回目 : 409ms
2回目 : 376ms
3回目 : 391ms
平均 : 392.0ms

FindObjectsByType
1回目 : 425ms
2回目 : 405ms
3回目 : 413ms
平均 : 414.3ms

# コード

```csharp:HogeClass0.cs
using UnityEngine; public class Hoge0Class : MonoBehaviour { }
```

```csharp:Measure.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using TMPro;
using UnityEngine;
using Random = System.Random;

public class Measure : MonoBehaviour
{
    List<GameObject> targetObjects = new();

    [SerializeField] TMP_Text resultOf;
    [SerializeField] TMP_Text resultBy;

    void OnGUI()
    {
        if (GUI.Button(new Rect(5, 5, 200, 20), "50個"))
        {
            SetupObjects(50);
        }
        
        if (GUI.Button(new Rect(5, 30, 200, 20), "500個"))
        {
            SetupObjects(500);
        }
        
        if (GUI.Button(new Rect(5, 55, 200, 20), "5000個"))
        {
            SetupObjects(5000);
        }
        
        if (GUI.Button(new Rect(5, 80, 200, 20), "計測"))
        {
            var sw = new Stopwatch();
            
            // FindObjectOfType
            sw.Start();
            for (var i = 0; i < MeasureCount; i++)
            {
                MeasureOf();
            }
            sw.Stop();
            resultOf.text += $"[OF] {sw.ElapsedMilliseconds}ms\n";
            
            sw.Restart();
            
            // FindObjectByType
            sw.Start();
            for (var i = 0; i < MeasureCount; i++)
            {
                MeasureBy();
            }
            sw.Stop();
            resultBy.text += $"[BY] {sw.ElapsedMilliseconds}ms\n";
        }
    }

    void SetupObjects(int num)
    {
        ResetObjects();
        CreateObjects(num);
    }

    void ResetObjects()
    {
        foreach (var targetObject in targetObjects)
        {
            Destroy(targetObject);
        }

        targetObjects = new List<GameObject>();
    }
    
    void CreateObjects(int num)
    {
        for (var i = 0; i < num; i++)
        {
            var go = new GameObject();
            
            // ランダムなクラスを3つ
            go.AddComponent(GetRandomType());
            go.AddComponent(GetRandomType());
            go.AddComponent(GetRandomType());
            
            targetObjects.Add(go);
        }
    }

    const int MeasureCount = 100;
    void MeasureOf()
    {
        var obj = FindObjectsOfType(GetRandomType());
    }
    
    void MeasureBy()
    {
        var obj = FindObjectsByType(GetRandomType(), FindObjectsSortMode.None);
    }

    static readonly Random Random = new();

    static Type GetRandomType()
    {
        return Classes.ElementAt(Random.Next(Classes.Count));
    }

    static readonly List<Type> Classes = new List<Type>
    {
        typeof(Hoge0Class),
        typeof(Hoge1Class),
        typeof(Hoge2Class),
        .....
    };
}

```