太郎Work

Unityとかで困ったこと等を残しておきます

GetComponentの負荷検証

気になる内容の記事があったので検証してみました
qiita.com

気になる点

  • Dictionaryのインデクサを使用している←TryGetValueを使用しないと負荷が高い(ソースは忘れた…)
  • transform.Rotationの実装次第ではあまり検証になっていない…気がする

ちょっと修正版作成

コードそのままお借りしてます+無理やり適当な名前で追加してすみません

極力transform取得の処理負荷が出るようにlocalPositionにしています

public class PerformanceTest : MonoBehaviour {
	private readonly Dictionary<Type, object> _componentCache = new Dictionary<Type, object>();

	public new T GetComponent<T>() where T : Component
	{
		var type = typeof(T);
		if (_componentCache.ContainsKey(type) == false)
		{
			var component = base.GetComponent<T>();
			if (component == null) component = gameObject.AddComponent<T>();

			_componentCache.Add(type, component);
		}

		return (T) _componentCache[type];
	}

	private readonly Dictionary<Type, Component> _componentCacheFast = new Dictionary<Type, Component>();
	public T GetComponentFast<T>() where T : Component
	{
		var type = typeof(T);
		Component component = null;
		if (_componentCacheFast.TryGetValue(type, out component) == false)
		{
			component = base.GetComponent<T>();
			if (component == null) component = gameObject.AddComponent<T>();

			_componentCacheFast.Add(type, component);
		}

		return (T) component;
	}

	private readonly int max = 10000;
	private CustomSampler samplerNormal;
	private CustomSampler samplerDefaultGetComponent;
	private CustomSampler samplerOverrideGetComponent;
	private CustomSampler samplerGetComponentFast;
	private CustomSampler samplerCache;
	private Transform cacheTransform;

	private void Awake()
	{
		samplerNormal = CustomSampler.Create("transform");
		samplerDefaultGetComponent = CustomSampler.Create("base.GetComponent");
		samplerOverrideGetComponent = CustomSampler.Create("GetComponent");
		samplerGetComponentFast = CustomSampler.Create ("GetComponentFast");
		samplerCache = CustomSampler.Create("cacheTransform");
		cacheTransform = base.GetComponent<Transform>();
	}

	private void Update()
	{
		Vector3 pos;
		//普通の使い方
		samplerNormal.Begin();
		for (var i = 0; i < max; ++i)
			pos = transform.localPosition;
		samplerNormal.End();

		//GetComponentおじさん
		samplerDefaultGetComponent.Begin();
		for (var i = 0; i < max; ++i)
			pos = base.GetComponent<Transform> ().localPosition;
		samplerDefaultGetComponent.End();

		//貼るだけで速くなるコード(?)
		samplerOverrideGetComponent.Begin();
		for (var i = 0; i < max; ++i)
			pos = GetComponent<Transform> ().localPosition;
		samplerOverrideGetComponent.End();

		//貼るだけで速くなるコードTryGetValue版
		samplerGetComponentFast.Begin();
		for (var i = 0; i < max; ++i)
			pos = GetComponentFast<Transform> ().localPosition;
		samplerGetComponentFast.End();

		//メンバ変数でキャッシュ
		samplerCache.Begin();
		for (var i = 0; i < max; ++i)
			pos = cacheTransform.localPosition;
		samplerCache.End();
	}
}

結果

丁度2018が正式リリースされていたので2018.1.0f2で検証
f:id:tarowork:20180503033428p:plain

transform 0.58ms
GetComponent 0.846ms
DictionaryIndexer 3.17ms
DictionaryTryGetValue 2.06ms
transformCache 0.399ms

TryGetValueにするだけで約65%軽減(どっちにしろ重いけど、、)
キャッシュとの違いは10倍近く

結論

何が言いたいかというとキャッシュはしたほうがいいよ
Dictionaryのインデクサは使わないほうがいいよ
という話でした