名古屋でアプリ・VR開発を行っています
ワクワクできるゲームをガンガン開発リリース中!

【Unity】実は関数を呼び出すのにコストがかかってた?!

みなさん、こんにちはSAT-BOXのアルパカです。

Unityで開発するときに絶対にお世話になる、C#なのですが、実は関数の呼び出しの時にコストがかかっていて重くなってたりしますので、今回は少しの工夫で関数の呼び出しのコストを少なくするやり方を描いていこうと思います!


まずこちらの
MethodImplAttribute クラスこちらクラスを描いているスクリプトを追加します。
こちらの機能は関数をインライン化(書いたものを効率よくコンピューターが変換してくれるもの)をしてくれるものとなります。
docs.microsoft.com


実装の仕方は関数の上に[MethodImpl(MethodImplOptions.AggressiveInlining)]こちらを追加するだけです!だがこれだけではエラーが出てしまいますのでusingでSystem.Runtime.CompilerServicesを書きましょう


試しにありとなしで比較するために書きましょう!

MethodImplAttributeあり↓

using System.Runtime.CompilerServices; //これが無いとMethodImplAttributeが使えない

public static class MethodImplAttributeON
{
    public static float x = 1;
    public static float y = 2;
    public static float z;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static float Test()
    {
        z += x + y;
        return z;
    }
}

MethodImplAttributeなし↓

public static class MethodImplAttributeOFF
{
    public static float x = 1;
    public static float y = 2;
    public static float z;

    public static float Test()
    {
        z += x + y;
        return z;
    }
}


これだけではわからないのでUnityの「Profiler」で確認できるスクリプトを作りましょう!

確認用のスクリプト

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;

public class MethodTest : 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 int max = 10000;
    private CustomSampler MethodImplAttribute_ON;
    private CustomSampler MethodImplAttribute_OFF;


    private void Awake()
    {
        MethodImplAttribute_ON = CustomSampler.Create("MethodImplAttributeあり");
        MethodImplAttribute_OFF = CustomSampler.Create("MethodImplAttributeなし");
    }

    private void Update()
    {
        //MethodImplAttributeあり
        MethodImplAttribute_ON.Begin();
        for (var i = 0; i < max; ++i) MethodImplAttributeON.Test();
        MethodImplAttribute_ON.End();

        //MethodImplAttributeなし
        MethodImplAttribute_OFF.Begin();
        for (var i = 0; i < max; ++i) MethodImplAttributeOFF.Test();
        MethodImplAttribute_OFF.End();
    }
}


実行してProfilerで確認してみると・・・

少しですがなんと減っています!

例で作ったものは少ししか変わらなかったですが大量に関数を使う方は使ってみるのはいかがでしょうか?
そして、こちらのクラスはC#だけの開発でも使えるので是非!


今回は軽量化につながる「MethodImplAttribute」のお話でした!

以上、アルパカでした!