[Unity] 続・GetIncetanceID での速度比較


# 序

[以前書いた記事](https://qiita.com/yk_golden_duck/items/cdcb23ee33d509643a19)では、GetIncetanceID と GetComponent の速度比較だったが、
```operator ==``` と比較してどちらが早いのか気になったので検証してみた。

MonoBehaviour には元々```operator ==```が実装されているので、
MonoBehaviour を継承しておけば、特に何もしなくてもオブジェクト同士を比較できる。

UnityEngine.Objectが継承元なのだが、UnityEngine.Objectを直接継承すると null 比較が出来ないようなので、
ご利用は計画的に、と言ったところだろうか。
(リファレンス:  https://docs.unity3d.com/ja/2018.4/ScriptReference/Object-operator_eq.html)

重要な検証内容というよりは、自身の戒めの為、記載しとこうと思う。

## 検証内容

検証内容はシンプルで、以下のようなループをただ回すだけ。
if文の中身を入れ替えてみて、速度の確認をする。

ループ回数は、1000万回。

```C#:loop
        for (int i = 0; i < Count; i++)
        {
            if (Sphere == this.gameObject)　// ここを入れ替える
            {
                dummy++;
            }
        }
```

今回は、5つのパターンで試してみた。

パターンは以下の5つ

1. GameObject 同士の比較で、this.gameObject で参照した場合
1. GameObject をそれぞれ GetIncetanceID して比較した場合
2. 比較元の片方の GetIncetanceID(int型) をキャッシュし、片方を GetIncetanceID した場合
3. 両方をキャッシュし、int型で比較した場合
4. GameObject を両方キャッシュし、==operator で比較した場合


## 結果

試行回数: 10,000,000 回
Sphere は Inspector 上にアタッチされたGameObject

|比較内容|時間|
|:-----------|------------:|:------------:|
|thisInstanceID2(Cashe int) == thisInstanceID(Cashe int) | 20ms|
|Sphere == thisGameObject(Cashe GameObject) | 61ms|
| Sphere.GetInstanceID() == thisInstanceID(Cashe int) |  189ms |
| Sphere == this.gameObject |  587ms| 
| Sphere.GetInstanceID() == this.gameObject.GetInstanceID() |  898ms| 
| (参考値) 前回の GetComponent で比較した場合 |  1627ms| 

結果を順に見ていこう。

#### 1位：GetInstanceID を両方をキャッシュし、int型で比較
まあこれは当然だろう。めっちゃ早い。

#### 2位：GameObject を両方キャッシュし、==operator で比較
これがまた早い。GetInstanceIDしないで、operatorで比較するべきという結果が出ている。

#### 3位：比較元の片方の GetIncetanceID(int型) をキャッシュし、片方を GetIncetanceID した場合
int 同士での比較して、純粋にGetIncetanceID自体の速度の目安になるかなと思って入れてみたテストだが、
Get の名前が示すように、一定の処理を通してIDを取得しているのが分かる。
可能ならoperatorで比較したほうが良いのだろう。

#### 4位：GameObject 同士の比較で、this.gameObject で参照した場合
これが意外にも遅かった。
個人的には this.gameObject で安易にアタッチ元を取得していたが、あまり良くない事が判明。
これは個人的には要反省だと思った。

#### 5位：GameObject をそれぞれ GetIncetanceID して比較した場合
.gameObject したうえで、GetIncetanceIDしているので、上位の結果を見れば当然遅い
GetComponent と比較してみて、半分程度の速度と考えると相当遅い処理になってしまっていることが分かる。



### 表にして整理
改めて、GetComponent と各要素との比較を表にして見る。

前回計測していた GetComponent は、Collider.gameObject.GetComponent<Dummy>()としていたので、
gameObjectを経由している部分を約500ms として、GetComponentのみを約1000ms とした。
あくまで見安として見ていただければと思う。

|各要素|時間(1000万回)|
|:-----------|------------:|:------------:|
|GetComponent|1000|
|this.gameObject|520|
|GetInstanceID|160|
|GameObject operator|60|
|int比較|20|

こう見ると、int型の比較が50回分がGetComponent1回だと考えると、
思ったより早いなぁという印象はある。
当然ループで使うと目に見えて遅いので、安易に使うべきではないが。

## まとめ

- MonoBehaviour.gameObject は遅い部類
- GetInstanceID よりは operator で比較したほうが良い

MonoBehaviour.gameObject って、Transformからでも参照できるので凄く使い勝手が良いのだけど、
安易に使うものではないですね。
反省です…。

ちなみに、int型通しの比較で片方を public にしたところ、3msほど早くなったので、
このレベルになるとメモリの配置位置とかで速度差が出そうですね。

自戒の意味で今回記事にしてみました。
MonoBehaviour.gameObject使ってしまっている方は頭の片隅に留めていただけると幸い。


## 比較用コード

なんの見どころもないベタ書き比較用ゴミコード。

<details><summary>テストコード</summary><div>

```C#:SameTest.cs

using System.Threading.Tasks;
using UnityEngine;

public class SameTest : MonoBehaviour
{
    public GameObject Sphere;

    async void Start()
    {
        await Task.Delay(2000);

        int Count = 10000000;

        int dummy = 0;

        var sw1 = new System.Diagnostics.Stopwatch();
        sw1.Start();
        for (int i = 0; i < Count; i++)
        {
            if (Sphere == this.gameObject)
            {
                dummy++;
            }
        }
        sw1.Stop();

        Debug.Log($"Sphere == this.gameObject : {sw1.ElapsedMilliseconds}ms");


        var sw2 = new System.Diagnostics.Stopwatch();

        sw2.Start();
        for (int i = 0; i < Count; i++)
        {
            if (Sphere.GetInstanceID() == this.gameObject.GetInstanceID())
            {
                dummy++;
            }
        }
        sw2.Stop();

        Debug.Log($"Sphere.GetInstanceID() == this.gameObject.GetInstanceID() : {sw2.ElapsedMilliseconds}ms");


        var sw3 = new System.Diagnostics.Stopwatch();

        int thisInstanceID = this.gameObject.GetInstanceID();

        sw3.Start();
        for (int i = 0; i < Count; i++)
        {
            if (Sphere.GetInstanceID() == thisInstanceID)
            {
                dummy++;
            }
        }
        sw3.Stop();

        Debug.Log($"Sphere.GetInstanceID() == thisInstanceID(Cashe int) : {sw3.ElapsedMilliseconds}ms");



        var sw4 = new System.Diagnostics.Stopwatch();

        int thisInstanceID2 = this.gameObject.GetInstanceID();

        sw4.Start();
        for (int i = 0; i < Count; i++)
        {
            if (thisInstanceID2 == thisInstanceID)
            {
                dummy++;
            }
        }
        sw4.Stop();

        Debug.Log($"thisInstanceID2(Cashe int) == thisInstanceID(Cashe int) : {sw4.ElapsedMilliseconds}ms");


        var sw5 = new System.Diagnostics.Stopwatch();
        var thisGameObject = this.gameObject;

        sw5.Start();
        for (int i = 0; i < Count; i++)
        {
            if (Sphere == thisGameObject)
            {
                dummy++;
            }
        }
        sw5.Stop();

        Debug.Log($"Sphere == thisGameObject(Cashe GameObject) : {sw5.ElapsedMilliseconds}ms");

    }
}

```

</div></details>