ユニツール

無知の知。

【Unity】Vector3.normalizedは無駄な負荷が掛かっている

概要

Vector3.normalizedは、2点の差を正規化して速度を掛けたり、内積を求める前に正規化したり、Vector操作の前処理で良く使わる機能です。 今回は、UnityのVector3.normalizedは値渡しが多くて無駄に負荷が掛かっているという話を聞いたので、Editor拡張の勉強がてら検証してみました。

検証

テストには、UnityEngine.Vector3にデフォルトで実装されているnormalizedと、自作のNormalizedExという2つを比べています。 テスト方法は、まず、10,000回実行した合計時間を1回分として算出します。
それを、1,000回繰り返し、1回に掛かった平均時間を結果とします。

以下、検証用コード

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using Extentions.UnityEngine.Vector;

public class TestWindow : EditorWindow
{
    // 実行時間履歴
    List<System.TimeSpan> data = new List<System.TimeSpan>();
    int execute_count = 1;

    // GUIパラメータ
    Vector2 scroll_position = new Vector2(0, 0);

    GUILayoutOption[] button_option =
{
        GUILayout.Width(200),
        GUILayout.Height(25)
    };

    [MenuItem("Tools/テストウィンドウ")]
    static void Open()
    {
        var window = CreateInstance<TestWindow>();
        window.Show();
    }

    private void OnGUI()
    {
        // グループ開始
        GUILayout.BeginVertical();
        {
            // ボタン表示
            GUILayout.BeginHorizontal();
            {
                if (GUILayout.Button("通常Normalized実行", button_option))
                {
                    for (int i = 0; i < execute_count; ++i)
                        data.Add(TestExec1());
                }
                if (GUILayout.Button("軽量化Normalized実行", button_option))
                {
                    for (int i = 0; i < execute_count; ++i)
                        data.Add(TestExec2());
                }
                if (GUILayout.Button("リセット", button_option))
                {
                    data.Clear();
                }
            }
            GUILayout.EndHorizontal();
            GUILayout.Label("実行回数を入力");
            execute_count = EditorGUILayout.IntField(execute_count);
            
            var avg = 0.0d;// 平均値
            scroll_position = GUILayout.BeginScrollView(scroll_position);
            {
                for (int i = 0; i < data.Count; ++i)
                {
                    GUILayout.Label(data[i].TotalMilliseconds.ToString());
                    avg += data[i].TotalMilliseconds;
                }
                avg = avg / data.Count;
            }
            GUILayout.EndScrollView();

            // 結果表示
            GUILayout.BeginVertical();
            {
                GUILayout.Label(string.Format("平均値:{0}ms", avg.ToString("0.0000")));
                GUILayout.Label(string.Format("試行回数:{0}",data.Count));
            }
            GUILayout.EndVertical();
        }
        GUILayout.EndVertical();
    }
    
    /// <summary>
    /// 通常Normalizedテスト
    /// </summary>
    /// <returns></returns>
    public static System.TimeSpan TestExec1()
    {
        int TEST_COUNT = 10000;
        Vector3 hoge = new Vector3(100, 100, 100);
        Vector3 temp = Vector3.zero;
        var sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        {
            for (int i = 0; i < TEST_COUNT; ++i)
            {
                temp = hoge.normalized;
            }
        }
        sw.Stop();
        return sw.Elapsed;
    }

    /// <summary>
    /// 軽量化Normalizedテスト
    /// </summary>
    /// <returns></returns>
    public static System.TimeSpan TestExec2()
    {
        int TEST_COUNT = 10000;
        Vector3 hoge = new Vector3(100, 100, 100);
        Vector3 temp = Vector3.zero;
        var sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        {
            for (int i = 0; i < TEST_COUNT; ++i)
            {
                temp = hoge.NormalizedEx();
            }
        }
        sw.Stop();
        return sw.Elapsed;
    }
}



namespace Extentions.UnityEngine.Vector
{
    public static class Extention
    {
        // 正規化(軽量版)
        public static Vector3 NormalizedEx(this Vector3 vec)
        {
            float len = Mathf.Sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
            return new Vector3(vec.x / len, vec.y / len, vec.z / len);
        }
    }

}

検証結果

比較対象 結果
UnityEngine.Vector3.normalized(デフォルト) 0.8505ms
NormalizedEx(自作) 0.5645ms

という結果になりました。 処理の内容自体はほぼ同じですが、約1.5倍の短縮になるのが分かりました。

UnityEngine.Vector3.normalized(デフォルト) f:id:sheena-hikari-games:20181221221357p:plain NormalizedEx(自作) f:id:sheena-hikari-games:20181221221400p:plain

まとめ

これだけで、劇的に早くなる!というほどではありませんが、他にも、事前に計算して置いたり、参照をキャッシュしたり、といった事を意識して作ればそれなりの効果にはなるのかな、と思います。 塵も積もれば、の精神で気を付けていきたい。