LoginSignup
19
16

More than 5 years have passed since last update.

GetComponent()の速度を改めて計測してみる

Last updated at Posted at 2016-02-13

先日Debug.Log() や Instantiate() などの速度を計測してみるという記事で普段使っているメソッドの速度を計測してみましたが、その中で1つ気になることがありました。それはGetComponent() は対象がいる場合といない場合で速度が違っていたことです。

そこで今回は GetComponent() に絞って検証してみました。
仮説はインスペクターでの表示順が GetComponent() での取得順に影響するというものです。
つまり、取得したいコンポーネントが TargetComponent クラスだった場合、これが上のほうにあればあるほど速度が増すのではないか、ということです。

getcomponent.png

ストップウォッチ

先日のようにOnGUI()を計測するわけではないので Update(), LateUpdate() を用いたストップウォッチにしました。
これで出力結果は純粋なメソッドの処理時間の平均になります。
他には特に特筆することはありませんが、一応コードを載せておきます。

StopWatch.cs
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System;

public class StopWatch : MonoBehaviour {

    private int tick = 0;
    private List<int> tickList = new List<int>();

    void Update () {
        tick = Environment.TickCount;
    }

    void LateUpdate()
    {
        var delta = Environment.TickCount - tick;

        tickList.Add(delta);

        // サンプリングを10000回.その後ログ出力.
        if(tickList.Count == 9999)
        {
            Debug.Log("----- Average -----");
            Debug.Log(tickList.Average().ToString("###.00"));
            Debug.Break();
        }
    }
}

テスト内容

テスト内容は以下のように設定しました。
・オブジェクトを1000個インスタンス化。それぞれのオブジェクトに GetComponentTest.cs を1つアタッチ
・すべてのオブジェクトに対してコンポーネントを1000個アタッチする。
 その際、GetComponent() 対象をアタッチするか、何番目にアタッチするかを設定できるようにする。
・アタッチするコンポーネントの中身は空にする ( TargetComponent.cs, DummyComponent.cs )
・ストップウォッチで10000回計測し、平均を出す

TestManager.cs
using UnityEngine;

public class TestManager : MonoBehaviour {

    [SerializeField]
    private GameObject pref;

    [SerializeField, ReadOnly]
    private int testObjCount = 1000;        // オブジェクト数.
    private const int compCount = 1000;     // Addするコンポーネント数.

    [SerializeField]
    private bool addTargetComponent = true; // GetComponent対象をAddするか

    [SerializeField, Range(0, compCount)]
    private int addIndex;                   // 何番目に対象コンポーネントをAddするか.

    void Awake()
    {
        for (int i = 0; i < testObjCount; i++)
        {
            var go = Instantiate(pref) as GameObject;
            go.AddComponent<GetComponentTest>();

            for (int j = 0; j < compCount; j++)
            {
                AddTestComponent(go, j);
            }
        }
    }

    void AddTestComponent(GameObject go, int index)
    {
        if (addTargetComponent &&
            index == addIndex)
        {
            go.AddComponent<TargetComponent>();
        }
        else
        {
            go.AddComponent<DummyComponent>();
        }
    }
}
GetComponentTest.cs
using UnityEngine;

public class GetComponentTest : MonoBehaviour {
    void Update () {
        GetComponent<TargetComponent>();
    }
}
TargetComponent.cs
using UnityEngine;

public class TargetComponent : MonoBehaviour { }
DummyComponent.cs
using UnityEngine;

public class DummyComponent : MonoBehaviour { }

結果

以下が計測結果です。
若干対象をアタッチしない方が1000番目(最後)にアタッチした場合より早くなっていますが、これはちょっとした揺らぎだと思われるので無視していいかと。

計測対象 処理時間
対象を1番目にアタッチする 1.58ms
対象を5番目にアタッチする 1.82ms
対象を100番目にアタッチする 4.61ms
対象を1000番目にアタッチする 32.55ms
対象をアタッチしない 32.54ms

以上の結果を鑑みると、 GetComponent() は対象オブジェクトにコンポーネントがアタッチされているか、頭から総当たりで探すメソッドである、ということが分かります。100個以上アタッチとかありえないと思うけど。対象がない場合が全体検索に匹敵するのは見つかるまで調べているからだった、ということですね。
前にぼくのブログでチラッと書いたGetComponentのイメージは当たってたということになります。やったぜ。

ともあれ、頻繁にアクセスするコンポーネントは上の方に配置するように意識するといいかもしれませんね。

19
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
16