Unity Profiler 入門:GetComponent / Debug.Log の計測と最新の実践法


Unity でパフォーマンスを意識したコーディングを行うには、Profiler を用いた計測が必須です。この記事では、以下を解説します。

  • Profiler の起動と基本操作
  • GetComponent を毎回呼ぶ場合とキャッシュする場合の差
  • Debug.Log の重さ
  • 最新の推奨 API(ProfilerMarker / ProfilerRecorder) の紹介
  • Editor 実行と Player 実行の違い に関する注意点

Profiler の使い方

Profiler ウィンドウを開く

メニューから Window → Analysis → Profiler を選択します。

Profiler ウィンドウは CPU、GPU、Memory、Rendering、など複数のカテゴリを確認できます。

計測対象の明示

コード内で負荷を囲みたい場合、従来は以下のように書いていました。

using UnityEngine.Profiling;

Profiler.BeginSample("負荷テスト");
// 計測したい処理
Profiler.EndSample();

ただし、Unity 2020以降は ProfilerMarker が推奨です。


ProfilerMarker の利用

BeginSample/EndSample は書き忘れリスクやオーバーヘッドが残るため、以下のように ProfilerMarker を使います。

using UnityEngine;
using Unity.Profiling;

public class ProfileTest : MonoBehaviour
{
    static readonly ProfilerMarker s_Marker = new ProfilerMarker("負荷テスト");
    Renderer _renderer;

    void Start()
    {
        _renderer = GetComponent<Renderer>(); // キャッシュ
    }

    void Update()
    {
        using (s_Marker.Auto())
        {
            for (int i = 0; i < 100000; i++)
            {
                // 計測対象コード
                var tmp = _renderer.enabled;
            }
        }
    }
}
  • using (s_Marker.Auto()) によって、範囲の開始・終了が確実に処理されます。
  • Release ビルドではオーバーヘッドはほぼゼロです。

GetComponent の比較

毎回呼ぶ場合

void Update()
{
    for (int i = 0; i < 100000; i++)
    {
        var rend = GetComponent<Renderer>();
        rend.enabled = true;
    }
}

キャッシュする場合

Renderer _renderer;
void Start() => _renderer = GetComponent<Renderer>();

void Update()
{
    for (int i = 0; i < 100000; i++)
    {
        _renderer.enabled = true;
    }
}

Profiler で比較すると、キャッシュした方が圧倒的に軽量であることが確認できます。

なお、実務では「すべてをキャッシュ」ではなく、頻繁に呼ぶもの・ホットパスのみキャッシュで十分です。


Debug.Log のコスト

void Update()
{
    for (int i = 0; i < 1000; i++)
    {
        Debug.Log("テスト");
    }
}
  • Profiler で確認すると、Debug.Log は非常に重い処理であることがわかります。
  • 大量出力はフレーム落ちや GC 負荷の原因になります。

実務的な対策

  • #if UNITY_EDITOR で Editor 実行時のみ有効化
  • 出力をバッファリング/まとめて書き出す
  • 必要なログは Analytics や独自カウンタに送る

Unity では #if UNITY_EDITOR という 条件付きコンパイルディレクティブ を使うことで、Editor 実行時のみ有効になるコードを書くことができます。


基本の使い方

using UnityEngine;

public class DebugExample : MonoBehaviour
{
    void Update()
    {
        // Editor 上でのみログを出したい場合
#if UNITY_EDITOR
        Debug.Log("Editor 実行時のみ表示されます");
#endif

        // 実機ビルドでは常に動かしたい処理
        transform.Rotate(Vector3.up * Time.deltaTime * 10f);
    }
}

動作

  • Unity Editor 内(Play 実行時) → Debug.Log が表示される
  • ビルドしたアプリ(Windows, Android, iOS, etc.) → Debug.Log の部分はコンパイル対象から外れる(完全に消える)

つまり「実機に余計なログ処理が入らない」ため、パフォーマンス低下や無駄な出力を防げるわけです。


よく使うパターン

1. ログ出力を Editor 限定にする

#if UNITY_EDITOR
Debug.Log($"座標: {transform.position}");
#endif

2. エディタ拡張用のコード

#if UNITY_EDITOR
using UnityEditor;  // Editor 名前空間はビルドに含められない

[CustomEditor(typeof(MyComponent))]
public class MyComponentEditor : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();
    }
}
#endif

UnityEditor 名前空間はビルドするとエラーになるため、必ず #if UNITY_EDITOR … #endif で囲みます。

3. 実行環境ごとに分ける

Unity には他にも多くの条件付きシンボルがあります。

  • UNITY_EDITOR : Editor 上で動作しているとき
  • UNITY_STANDALONE : Windows/Mac/Linux プレイヤー
  • UNITY_IOS, UNITY_ANDROID : モバイル
  • DEVELOPMENT_BUILD : 「Development Build」でビルドしたとき

組み合わせれば「Editor のときだけログ」「モバイルのときだけ処理変更」といった分岐が可能です。


注意点

  • #if UNITY_EDITOR で囲んだコードは ビルド後のアプリには一切含まれません
  • 「開発中だけ欲しいログ」「エディタ拡張専用コード」を入れるときに便利
  • 本番環境で必要な処理(例:エラーログ送信)は UNITY_EDITOR ではなく Debug.isDebugBuild や DEVELOPMENT_BUILD を使った方が安全

まとめ

  • #if UNITY_EDITOR は Editor 専用コード を安全に書くための仕組み
  • Debug.Log や UnityEditor 名前空間の利用に必須
  • 実機ビルドには含まれないため、パフォーマンスや安全性に優れる

これを使えば、わざわざ毎回 #if UNITY_EDITOR を書かなくても済みます。


EditorLog.cs(簡易ラッパークラス)

using UnityEngine;

public static class EditorLog
{
    /// <summary>
    /// Editor 実行時のみログを出力します
    /// </summary>
    public static void Log(object message)
    {
#if UNITY_EDITOR
        Debug.Log(message);
#endif
    }

    /// <summary>
    /// Editor 実行時のみ警告ログを出力します
    /// </summary>
    public static void LogWarning(object message)
    {
#if UNITY_EDITOR
        Debug.LogWarning(message);
#endif
    }

    /// <summary>
    /// Editor 実行時のみエラーログを出力します
    /// </summary>
    public static void LogError(object message)
    {
#if UNITY_EDITOR
        Debug.LogError(message);
#endif
    }
}

使い方

using UnityEngine;

public class Sample : MonoBehaviour
{
    void Start()
    {
        // Editor でのみ表示される
        EditorLog.Log("Editor でだけ出力されます");
        EditorLog.LogWarning("警告です");
        EditorLog.LogError("エラーです");
    }
}
  • Unity Editor の Play 実行 → ログが表示される
  • ビルドした実機アプリ → ログ処理自体がコンパイルから除外される

利点

  • #if UNITY_EDITOR を毎回書かなくていい
  • コードがすっきりして読みやすくなる
  • 実機ビルドには一切影響を与えない

ProfilerRecorder によるスクリプト計測

Unity 2020.2 以降、ProfilerRecorder を使って スクリプトから統計データを取得できます。これにより、Profiler ウィンドウを開かなくても HUD 表示が可能です。

using Unity.Profiling;

public class GCAllocMonitor : MonoBehaviour
{
    ProfilerRecorder _gcRecorder;

    void OnEnable()
    {
        _gcRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "GC Allocated In Frame");
    }

    void Update()
    {
        Debug.Log($"GC Alloc: {_gcRecorder.LastValue / 1024f} KB");
    }

    void OnDisable() => _gcRecorder.Dispose();
}

Editor 実行と Player 実行の違い

Profiler の結果は Editor 実行では正確ではない場合があります。

Editor のオーバーヘッドが「Other」などに大きく乗るため、実際より重く見えることもあります。

注意点

  • 開発中の目安は Editor Profiler
  • 最終的な判断は Development / Release Player ビルドで確認すること

Deep Profiling の注意

Deep Profiling は関数ごとの詳細な計測が可能ですが、極端なオーバーヘッドを生むため結果が歪みやすいです。

利用は「特定のボトルネック調査の一時的な有効化」に留めましょう。


まとめ

  • Profiler を使って実際に計測して判断することが重要。
  • GetComponent はキャッシュが基本。
  • Debug.Log は極めて重いので乱用禁止。
  • ProfilerMarker / ProfilerRecorder を使えば、最新の Unity で安全かつ効率的な計測が可能。
  • Editor と Player で結果が違う点を必ず意識する。

これらを踏まえて、日常的に「なんとなくではなく数値で判断する」習慣をつけていきましょう。


訪問数 15 回, 今日の訪問数 15回

Profiler,Unity

Posted by hidepon