【Unity】Instantiate()とSetParent()などの処理速度を計測してみる

Unityにおいて、Instantiate()でのオブジェクトの複製はそこそこ重い処理であるから処理を軽量化したいなーって思って色々と調べていたら以下のような記述を見つけた。

- 複製したオブジェクトの親は直接設定してあげるより、SetParent()で設定する方が高速
- Instantiateの引数で親を設定してやると爆速化する

Instantiate()した後は多くの場合親設定も同時に行うと思うので、親の設定方法を変えるだけで高速化するのなら嬉しい。
しかし、肝心のどのくらい高速になるのかが分からなかったので計測してみることにした。
なお、この手の検証及びQiitaへの記事投稿は今回が初めてなのでそこはご容赦して頂きたい。

#計測用のコード
以下のコードを用いてInstantiate()時の親設定の速度を計測した。

```c#:InstantiateTest.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InstantiateTest : MonoBehaviour
{
	private System.Diagnostics.Stopwatch Sw = new System.Diagnostics.Stopwatch ();//計測用ストップウォッチ
	public GameObject InstantiateObject;//Instantiateでの複製元のオブジェクト、今回はPrefabsに設置した空のゲームオブジェクト
	public Transform InstantiateParent;//Instantiateで複製したオブジェクトの親
	private GameObject Clone;//Instantiateで複製されたオブジェクト
	private int i;
	private readonly int n = 10000;//ループ回数

	private void Awake ()
	{
		Sw.Start ();//ストップウォッチを開始する
		for (i = 0; i < n; i++)//n回ループする
		{
			//この下に処理を書く
			Instantiate1 ();
			//Instantiate2 ();
			//Instantiate3 ();
			//Instantiate4 ();
		}
		Sw.Stop ();//ストップウォッチを終了する
		Debug.Log (Sw.Elapsed.TotalMilliseconds + "ms");//ログに上の処理にかかった時間(ms)を表示する、数値型はdoubleなのでかなり精密
	}

	//複製後に普通に親を設定する
	private void Instantiate1 ()
	{
		Clone = Instantiate (InstantiateObject);
		Clone.transform.parent = InstantiateParent;
	}

	//複製後にSetParentで親を設定し、第二引数をtrueにする
	//※trueである場合、 親との相対的な位置、角度、スケールは、設定される前のオブジェクトのワールド空間の位置、角度、スケールを維持して変更されます。(Unity公式から引用)
	private void Instantiate2 ()
	{
		Clone = Instantiate (InstantiateObject);
		Clone.transform.SetParent (InstantiateParent, true);
	}

	//複製後にSetParentで親を設定し、第二引数をfalseにする
	private void Instantiate3 ()
	{
		Clone = Instantiate (InstantiateObject);
		Clone.transform.SetParent (InstantiateParent, false);
	}

	//複製時に親を設定する
	private void Instantiate4 ()
	{
		Clone = Instantiate (InstantiateObject, InstantiateParent);
	}
}
```

コードの動作はコメントで説明してあるのでここでは省略する。
ここで言うべきはInstantiateObjectとInstantiateParentはエディター上で設定してある事、計測時はfor文の内部のコメントアウトを変えて実行するメソッドを変えた事ぐらいか。
InstantiateObjectとInstantiateParentは下図のように空っぽのオブジェクトである。
![InstantiateObject](https://qiita-image-store.s3.amazonaws.com/0/318712/bfbe51a1-9b8c-82fd-ab45-9676446a2524.png) ![InstantiateParent](https://qiita-image-store.s3.amazonaws.com/0/318712/de872770-ef77-4b68-76c3-f9f810e0c39b.png)

#計測結果
n=100,1000,10000の時について、コードにある4つの関数に加えて何も実行しないパターンの5パターンをそれぞれ10回実行し、平均を取った。
結果を纏めた表は以下の通り。

|実行関数|無し|Instantiate1()|Instantiate2()|Instantiate3()|Instantiate4()|
|:------------:|:------------:|:------------:|:------------:|:------------:|:------------:|
|n=100|0.00206ms|1.4741ms|1.29668ms|1.26086ms|1.06012ms|
|n=1000|0.00404ms|7.78604ms|7.64797ms|7.40351ms|5.55308ms|
|n=10000|0.03287ms|75.09554ms|71.85158ms|68.46317ms|49.39062ms|

nの値、つまり複製個数に関わらず処理速度の速さはInstantiate4()>Instantiate3()>Instantiate2()>Instantiate1()となった。
Instantiate1()とInstantiate2()、Instantiate3()を比べてみると普通に親を設定するよりSetParentの方が処理時間が9割前後で済むようだ。高速になったと言えるかは微妙だが速くなった事は間違いない。SetParentの引数もfalseの方が僅かに高速化するが、正直誤差レベルだろう。

問題はInstantiate4()、つまりInstantiate()実行時に親も同時に設定してやる場合である。n=100では微妙だがn=1000以降では目に見えて高速化している。処理時間は普通に親を設定する場合の3分の2、SetParentの4分の3程度で済んでいる。処理速度にして普通に親を設定する場合の1.5倍、SetParentの1.33倍である。確かにめっちゃ早くなっている。

ちなみに計測結果を纏めたエクセルの表は以下のようになっている。
![計測結果のエクセル](https://qiita-image-store.s3.amazonaws.com/0/318712/1d09507d-ad06-ffa7-1763-ae76615c9bbd.png)

#結論
<font color="Red">**Instantiate()関数実行時に親も同時に設定してやると滅茶苦茶高速になる。**</font>

処理速度にして普通に親を設定する場合の約1.5倍、SetParent()で設定する場合の約1.33倍になる。今回の検証では最大でも空のオブジェクトを1万個作るだけだったが、オブジェクトを数百万個作る場合や大きめのオブジェクトを複製する場合は実際の体感時間にも響いて来ると思うので是非とも高速化したい。
しかしInstantiate()関数実行時に親を設定する事には1つ欠点がある。親との相対的な位置関係をうまく操作できない点だ。ちょっと調べたらSetParent()の第二引数がtrueのような動作をするらしい。