ドキュメント

Unity Engine

Unity Engine

プロファイラー出力の分析

Investigate common startup and runtime performance issues by analyzing Profiler traces.
読み終わるまでの所要時間 16 分最終更新 1ヶ月前

アプリケーションをプロファイルする時に、いくつかの一般的な問題が発生する可能性があります。このページでは、一般的なパフォーマンス問題の原因を調査する方法について説明します。

起動時の出力の分析

起動時間のプロファイリング出力を確認する場合、主に調査すべきメソッドは 2 つあります。
UnityInitApplicationGraphics
と
UnityLoadApplication
です。プロジェクトの設定、アセット、コードによる起動時間への影響に関しては、主にこの 2 つのメソッドが原因となります。
注意アプリケーションの起動時間はプラットフォームによって異なります。ほとんどのプラットフォームでは、起動は スプラッシュスクリーン の表示中に行われます。

iOS デバイスで実行したサンプル Unity プロジェクトの Instruments の出力

上の iOS デバイスで実行したサンプル Unity プロジェクトの Instruments の出力のスクリーンショットで、プラットフォーム別の
startUnity
メソッド内の、
UnityInitApplicationGraphics
メソッドと
UnityLoadApplication
メソッドに注目してください。
UnityInitApplicationGraphics
は多くの内部機能を実行します。例えばグラフィックスデバイスの設定や、多くの Unity 内部システムの初期化などです。また、リソースシステム に含まれる全てのファイルのインデックスをロードすることで、リソースシステムの初期化も行います。
Unity の リソースシステム には、プロジェクトの
Assets
フォルダー内の
Resources
フォルダーにあるデータ内の全ての アセットファイル が含まれます。これには、
Resources
フォルダーの子フォルダーにある全てのファイルが含まれます。したがって、リソースシステムの初期化に掛かる時間は、アプリケーションのプロジェクトの
Resources
フォルダー内のファイル数に応じて直線的に増加します。
UnityLoadApplication
にはプロジェクトの最初のシーンをロードして初期化する (複数の) メソッドが含まれます。これには最初のシーンの表示に必要なデータのデシリアライゼーションとインスタンス化 (シェーダーのコンパイリング、テクスチャのアップロード、ゲームオブジェクトのインスタンス化など) が含まれます。また、Unity は最初のシーン内の全ての
MonoBehaviour
の
Awake
コールバックを実行します。
上記のようなプロセスになっているということは、つまり、プロジェクトの最初のシーン内の
Awake
コールバック内に長時間掛かるコードが 1 つでもある場合、そのコードが原因でプロジェクトの最初の起動時間が長くなっている可能性があるこということです。これを解決するには、時間の掛かるコードを削除するか、そのコードをアプリケーションのライフサイクル内の他の場所で実行するようにする必要があります。

ランタイムの出力の分析

最初の起動時間の後にキャプチャされたプロファイリング出力に関しては、主に注目すべきは
PlayerLoop
メソッドです。これは Unity のメインループで、中にあるコードが 1 フレームに 1 回実行されます。

サンプル Unity プロジェクトの Instruments の出力

上のスクリーンショットは、
PlayerLoop
内でパフォーマンスに最も影響を与えるいくつかのメソッドを示しています。注意
PlayerLoop
内のメソッドの名前は Unity のバージョンによって異なる可能性があります。
PlayerRender
は、Unity のレンダリングシステムを実行するメソッドです。これにはオブジェクトのカリング、動的バッチの計算、GPU への描画指示のサブミットなどが含まれます。イメージエフェクトや、レンダリングベースのスクリプトコールバック (例えば、OnWillRenderObject) も全てここで実行されます。基本的に、プロジェクトがインタラクティブな時に最も CPU 時間を消費するのがこのメソッドです。
BaseBehaviourManager
はテンプレート化された 3 つのバージョンの
CommonUpdate
を呼び出します。これらは、現在のシーン内のアクティブなゲームオブジェクトに添付された
MonoBehaviour
内の特定のコールバックを実行します。
  • CommonUpdate<UpdateManager>
    は
    Update
    コールバックを呼び出します。
  • CommonUpdate<LateUpdateManager>
    は
    LateUpdate
    コールバックを呼び出します。
  • CommonUpdate<FixedUpdateManager>
    は、物理演算システムにチェックマークが入っている場合に
    FixedUpdate
    を呼び出します。
調査する上で基本的に最も便利なメソッドファミリは
BaseBehaviourManager::CommonUpdate<UpdateManager>
です。これが Unity プロジェクト内で実行される大部分のスクリプトコードのエントリーポイントだからです。
他にも、調査に役立つメソッドがいくつかあります。
  • UI::CanvasManager
    は、プロジェクトが UGUI システム を使用している場合に、いくつかの異なるコールバックを実行します。これには Unity UI のバッチ計算やレイアウト更新が含まれます。プロファイラーに
    CanvasManager
    が表示される場合、その原因として最もよく見られるのは、この 2 つの処理です。
  • DelayedCallManager::Update
    は コルーチン を実行します。
  • PhysicsManager::FixedUpdate
    は PhysX 物理演算システムを実行します。これは主に PhysX の内部コードを実行することによって行われます。
    Rigidbody
    や
    Collider
    など、現在のシーン内にある物理演算オブジェクトの数は、PhysX の内部コードに影響します。物理演算ベースのコールバックもここに表示されます。具体的には、
    OnTriggerStay
    と
    OnCollisionStay
    です。
プロジェクトが 2D 物理演算を使用している場合、それは、一式の類似したコールとして
Physics2DManager::FixedUpdate
の下に表示されます。

スクリプトメソッドの分析

スクリプトが IL2CPP を用いてクロスコンパイルされてプラットフォーム上で実行されている場合、
ScriptingInvocation
オブジェクトの含まれる出力ラインを探してください。そこが、Unity の内部ネイティブコードが、スクリプトコードを実行するためにスクリプトランタイムへ遷移している箇所です。注意技術的には、Unity が IL2CPP 経由で実行した後は、C# コードもネイティブコードとなります。ただしこのクロスコンパイルコードは、主に IL2CPP ランタイムフレームワークでメソッドを実行しており、手書きの C++ とは類似しません。

サンプル Unity プロジェクトからの出力

上のスクリーンショットで、
RuntimeInvoker_Void
ラインの下にネストされたメソッドは、1 フレーム毎に 1 回実行されるクロスコンパイル済み C# スクリプトの一部です。
出力ラインの名前は、元のクラス名、下線、元のメソッド名の順で連なった形になっています。このサンプルの出力では、
EventSystem.Update
、
PlayerShooting.Update
、その他いくつかの
Update
メソッドが確認できます。これらは、ほとんどの
MonoBehaviours
内に見られる、Unity の標準の
Update
コールバックです。
これらのメソッドを展開すると、その中のどのメソッドが CPU 時間を消費したかを確認できます。これは、プロジェクト内、Unity API 内、C# ライブラリコード内のその他のスクリプトメソッドに関しても同様に行えます。
上の出力からは、
StandaloneInputModule.Process
メソッドが 1 フレーム毎に 1 回、UI 全体にレイキャストを行っていたことが確認できます。このメソッドは、何らかのタッチイベントが UI 要素上をホバリングしたり要素をアクティベートしたりしていたかどうかを検知します。全ての UI 要素に対してメソッドを繰り返し、マウスの位置がその境界矩形内にあるかどうかをテストすると、高いリソース負荷が掛かります。

アセットの読み込み

CPU の出力内でアセットのロードを特定することもできます。アセットのロードを示す主要なメソッドは
SerializedFile::ReadObject
です。このメソッドは特定のファイルからのバイナリデータストリームを、
Transfer
という名前のメソッドによって実行される Unity のシリアル化システムに接続します。
Transfer
メソッドは、テクスチャ、MonoBehaviour、パーティクルシステムを始めとする全てのアセットタイプに見られます。

シーンのロードの出力

上のスクリーンショットは、Unity がシーンをロードした出力です。シーンをロードすると、Unity はシーン内の全てのアセットを読み込み、デシリアライズします。これは、
SerializedFile::ReadObject
の下にあるさまざまな
Transfer
メソッドのコールによって示されます。
ランタイムにパフォーマンススタッターが発生し、パフォーマンス出力で
SerializedFile::ReadObject
がかなりの時間を費やしたことが示されている場合は、アセットのロードによってフレームレートが減少したことを意味します。ノート:
SerializedFile::ReadObject
は通常、
SceneManager
、
Resources
、または AssetBundle API がアセットの同期的ロードをリクエストすると、メインスレッドに表示されます。
このパフォーマンススタッターを解決するには、アセットのロードを非同期にする (重い
ReadObject
コールがワーカースレッドに移される) か、一部の重いアセットをプリロードしてください。
Transfer
コールは、Unity がオブジェクトをクローン (複製) する場合にも現れます (出力内では
CloneObject
メソッドによって示されます)。
Transfer
への呼び出しが
CloneObject
コールの下に表示される場合、Unity は該当アセットをストレージからロードしていません。代わりに、Unity は古いオブジェクトのデータを新しいオブジェクトに送信します。Unity はこれを行うために、古いオブジェクトをシリアル化し、その結果のデータを新しいオブジェクトとしてデシリアライズします。