[Unity初心者必見]Debug.Logは直接使ってはいけない

C#

開発初期だとprintデバッグだったり、とりあえずlog出しておくためにDebug.Log系の関数を使っていると思います。

しかしこのDebug.Log系関数、そのまま使うことはおすすめしません

パフォーマンス問題

以下のコードを見てください。

void Update(){
Debug.Log(Time.realtimeSinceStartup.ToString());
}

このコードはパフォーマンス視点から見ると非常に悪いコードです。

ためしにprofilerでwindows buildをプルファイルしてみましょう。

一回の呼び出しで0.46ms、5.9KB のgc allocです。ありえないくらい重いです。

ループの中で何百回と呼び出されるとそれだけで秒単位でゲームが止まります

Debug.Log呼び出しを消せない

http://tsubakit1.hateblo.jp/entry/2016/03/28/231829
unityのドキュメントやテラシュールブログ記事を見て、Debug.Log呼び出しを消せるような気がしちゃいますが、消せません。順にみていきましょうか。

loggingをNoneにしてプロファイルしてみます。


先ほどよりはCPU負荷、gc allocが減りましたが、消えてはいません。

これは、logging.None ScriptOnly Fullが単にスタックトレースの程度を調整する機能だからです。

この場合、スタックトレースが消えた分負荷が減っただけです。

次にログ表示をスクリプトから消してみます。

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void Init()
    {
        Debug.unityLogger.logEnabled = false; // ←ログを止める
    }

CPU負荷がほぼなくなりましたが、gc allocはまだ残ってます

Debug.unityLogger.logEnabled = falseは、Consoleへのログ出力をやめます。しかしDebug.Log呼び出し自体はおこなわれています

Debug.Logの引数に渡しているTime.realtimeSinceStartup.ToString() という処理が、呼ばれるたびにStringクラスのインスタンスをnewしているためgc allocが残っています。

現状のunityでは、これを消すことはできません。

ということはリリースビルドで毎フレーム12Bのgc allocが発生し、ガベージコレクションの呼び出し可能性が上がり、ゲームが停止する可能性があがることになります。

まとめ

Debug.Logはパフォーマンスがとても悪いので、基本は一時的なデバッグ用途で使うことが多いです。

それでも、エラー検出のためにDebug.LogWarningやDebug.LogErrorを使いたいことがあると思います。

それについては、ゲーム向けのハイパフォーマンスな実装方法があるので後で別記事にまとめます(たぶんnoteに書きます。