TryGetComponent() と比べたら GetComponent() を使う理由がなくなった件

2022/06/09に公開

Unity2019.2 から GetComponent() とほぼ同機能の TryGetComponent() が追加されています。
文法的な差異くらいなのかなと軽くスルーしてたのですが、

TryGetComponent() は alloc しない

という違いがあるらしいです。
(正確には、エディタ上で対象のコンポーネントがなかったときだけ GetComponent() は alloc する[1]

となると、あとはパフォーマンスが気になるところです。

パフォーマンス比較

次のようなコードで1000個の GameObject からコンポーネントを取得してみました。
環境は Unity2021.3.2f1、Win10 です。

void Profile_GetComponent()
{
    Profiler.BeginSample(nameof(Profile_GetComponent));

    for (var i = 0; i < count; ++i)
    {
        var sampleComponent = _goList[i].GetComponent<SampleComponent>();
        if (sampleComponent != null)
        {
            sampleComponent.value++;
        }
    }

    Profiler.EndSample();
}
void Profile_TryGetComponent()
{
    Profiler.BeginSample(nameof(Profile_TryGetComponent));

    for (var i = 0; i < count; ++i)
    {
        if (_goList[i].TryGetComponent<SampleComponent>(out var sampleComponent))
        {
            sampleComponent.value++;
        }
    }

    Profiler.EndSample();
}

コード全容

結果はこちら


コンポーネント有りエディター


コンポーネント有りビルド後


コンポーネント無しエディター


コンポーネント無しビルド後

TryGetComponent() のほうがちょっと速い!
たしかに「コンポーネント無しエディター」で GC Alloc がなく劇的に速い!

というわけで

GetComponent() を使う理由がなくなった気がするので常に TryGetComponent() で良いかなと思いました。
とはいえ GetComponent() をそんなにぶん回すこともないと思うので、パフォーマンスが気になる部分でなければどちらでもいい気もします。コンポーネントが確実にある想定なら GetComponent()、無いケースも想定してるなら TryGetCompoenent()、みたいな意味付けもありかもしれません。

ちなみに

パフォーマンスについてググったところテラシュールブログさんの記事にたどり着いたのですが、
よく見ると「コンポーネント無しエディター」の TryGetCompoenent()でも GC Alloc が発生していてドキュメントの記述と異なるのが気になりました。
もしかしたら当時と今の Unity で挙動が異なってるかもと思い、上記実験をしてみました。
実験内容を思いっきり参考にさせていただきました。感謝。

参考

https://tsubakit1.hateblo.jp/entry/2019/07/16/233235
https://baba-s.hatenablog.com/entry/2019/10/28/100000

脚注
  1. The notable difference compared to GameObject.GetComponent is that this method does not allocate in the editor when the requested component does not exist.
    https://docs.unity3d.com/ja/current/ScriptReference/GameObject.TryGetComponent.html

    ↩︎

Discussion