Unityな日々(Unity Geek)

Unityで可視化アプリを開発するための試行錯誤の覚書

角度指定と外部呼出しに関するベンチマーク

Unityでは、角度は、オイラー角とクォータニオンの2つの方法で扱える。また、回転を設定する際、角度の絶対値と、Rotate()で相対角変化を与える方法がある。いったいどれがもっとも効率的、すなわち高速なのだろうか?

また、あるオブジェクトのクラスから他のオブジェクトを操作する際、操作する側のクラスに操作される側のGameObjectなりTransformコンポーネントを保持して操作を加えるのと、操作される側にクラスを作ってそのPublicメソッドを呼び出すのと、どちらが速いのだろう。

そういう疑問がおきたので、ベンチマークしてみた。

比較結果

ベンチマークスクリプトは下に記す。各方法とも10万回ループの経過時間。

No. 処理内容 時間(mSec)
1-1 オイラー角で設定する 130
1-2 Rotate()関数を使って相対的にまわす 146
1-3 クォータニオンで設定する 123
1-11 ターゲット側のPublic関数に角度をを渡す(外部関数の中身は1-1と同等) 129
1-12 ターゲット側のPublic関数に角度ベクトルをを渡す(外部関数の中身は1-1と同等) 130
1-1A 1-1と同じ処理で、毎回transformコンポーネントを探す 139
1-12A 1-12と同じ処理でループの中で毎回同階層にある外部クラス(コンポーネント)を接続する 191
1-12B 1-12と同じ処理でループの中で毎回外部GameObjectを探す 241
結論
  • 角度の指定はEulerでもQuaternionでも、違いはそんなにない。Quaternionのほうが若干(5%程度) 速い程度。
  • 回転の際、角度を直接指定するより、Rotate()を使うほうが10%程度時間がかかる。
  • Public関数の呼び出しはコストがかからない。つまり、外部コンポーネントを自クラスに取り込んで自クラス内で操作しても、外部コンポーネントのPublic関数を呼び出してその中で操作しても、時間に差はない。
  • (やはり)外部コンポーネント・外部クラスの呼び出しは時間がかかる。呼び出しは最初にやって内部変数にリンクを格納しておくのがベター
ベンチマークスクリプト
// Bench Mark for Rotation & Function Call
// First Version 2015/03/14 by A.Y.@XOOMS
// Last Update   2015/mm/dd by A.Y.@XOOMS
//
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using Xooms;

namespace Xooms{
    public class BenchMark1 : MonoBehaviour {
        
        //Public Variables
        public Transform targetTrans;

        //Private Variables
        GameObject targetGameObject;
        bool firstTime = true;
        Stopwatch sw = new Stopwatch();
        int maxItr = 100000;
        float delA = 1.0f;
        DummyCube dummyCube;

        //Public Functions

        //--------------
        // Initialization
        void Start () {
            targetGameObject = targetTrans.gameObject;
            dummyCube = targetTrans.GetComponent<DummyCube>();
        }
    
        //
        void Update () {
            if(firstTime) {
                firstTime = false;
                //1.回転関連ベンチマーク
                //1-1 オイラー角で設定する
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    Vector3 v = new Vector3(0.0f, i*delA, 0.0f);
                    targetTrans.eulerAngles = v;
                }
                sw.Stop();
                DebugOut("1-1",sw.ElapsedMilliseconds);
                //
                //1-2 Rotate()関数を使って相対的にまわす
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    targetTrans.Rotate(i*delA*Vector3.up);
                }
                sw.Stop();
                DebugOut("1-2", sw.ElapsedMilliseconds);
                //
                //1-3 クォータニオンで設定する
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    targetTrans.rotation = Quaternion.Euler(0.0f, i*delA, 0.0f);
                }
                sw.Stop();
                DebugOut("1-3", sw.ElapsedMilliseconds);
                //
                //1-1A 1-1と同じ処理で、毎回transformコンポーネントを探す
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    Vector3 v = new Vector3(0.0f, i*delA, 0.0f);
                    targetGameObject.transform.eulerAngles = v;
                }
                sw.Stop();
                DebugOut("1-1A",sw.ElapsedMilliseconds);

                //
                //1-11 ターゲット側のPublic関数に角度をを渡す(外部関数の中身は1-1と同等)
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    dummyCube.RotateByAngle(i*delA);
                }
                sw.Stop();
                DebugOut("1-11",sw.ElapsedMilliseconds);

                //1-12 ターゲット側のPublic関数に角度ベクトルをを渡す(外部関数の中身は1-1と同等)
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    Vector3 v = new Vector3(0.0f, i*delA, 0.0f);
                    dummyCube.RotateByVector(v);
                }
                sw.Stop();
                DebugOut("1-12",sw.ElapsedMilliseconds);

                //1-12A 1-12と同じ処理でループの中で毎回外部クラス(コンポーネント)を探す
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    DummyCube target = targetTrans.GetComponent<DummyCube>();
                    Vector3 v = new Vector3(0.0f, i*delA, 0.0f);
                    target.RotateByVector(v);
                }
                sw.Stop();
                DebugOut("1-12A",sw.ElapsedMilliseconds);

                //1-12B 1-12と同じ処理でループの中で毎回外部GameObjectを探す
                sw.Reset();
                sw.Start();
                for(int i = 0; i < maxItr; i++) {
                    GameObject go = GameObject.Find("DummyCube");
                    DummyCube target =go.GetComponent<DummyCube>();
                    Vector3 v = new Vector3(0.0f, i*delA, 0.0f);
                    target.RotateByVector(v);
                }
                sw.Stop();
                DebugOut("1-12B",sw.ElapsedMilliseconds);
            }
        }

        private void DebugOut(string title, float timeMsec){
            UnityEngine.Debug.Log(title+ ": "+timeMsec.ToString("f1")+" msec");
        }
    }
}