電通総研 テックブログ

電通総研が運営する技術ブログ

UE5でプロファイリングを行う 〜CPU編〜

こんにちは、ISID 金融ソリューション事業部の岡崎です。
今回は前回に引き続き、
UE5でパフォーマンスを担保するために必要な、プロファイリングのワークフローCPU編の説明を行います。

UEでは、制作したプロジェクトの性能を測るためにプロファイリングという作業を行います。
プロファイリングは、主にパフォーマンス改善を目的として実施します。例えばコマンドや専用のアプリケーションを使用して、描画に遅延が起こっていないか、不要な処理をしているBlueprintがないか、などを調べます。

前回の記事(プロファイリングのワークフローGPU編)はこちらになります。

検証環境/ツール

実装手順

  1. 「stat unit」コマンドの実施
  2. Gamesの処理が重い場合の対処方法
    2-1. Unreal Insightsの使い方
    2-2. Session Frontendの使い方
    2-3. 遅延原因の特定方法

1. 「stat unit」コマンドの実施

前回に引き続き、「stat unit」コマンドについて説明します。
前回の記事(プロファイリングのワークフローGPU編)に詳しい「stat unit」の紹介があるのでそちらも参考にしてください。

GPU編でも紹介しましたが、「stat unit」の主要な項目は下記になります。

  • Frame : 画面を描画するのにかかった処理時間。60FPSのゲームを作成する場合は、16.6ms以下になるように調整しないといけない
  • Game : リアルタイムで動作するゲームのロジックやアニメーションにかかるCPUの処理時間
  • Draw : 何を描画するか、選択や計算をするためにかかるCPUの処理時間
  • GPU Time : メッシュや各マテリアルの描画や、ライティングなどにかかるGPUの処理時間
  • Draws : 描画する必要のあるメッシュの個数。GPU Timeに影響を与える

対象オブジェクトを整理したり、Naniteを利用してメッシュ数の調整を行うことで、GPUに関連する値を下げることはできましたが、依然として「Game」の値が高いので、調査、修正を行っていきます。

2. Gamesの処理が重い場合の対処方法

Gameの処理が重い場合は2つの事象が考えられます。

  • カリングやLODなどを使用してレベル内のどのオブジェクトを描画するか、事前に計算するためにCPUを使用している
  • ゲームプレイ中のキャラクターの動作や、アクター、その他のBlueprintの処理や計算をするためにCPUを使用している

今回はゲームプレイ中のBlueprintの挙動について、プロファイリング方法を紹介します。
カリングやLODに関しては、本記事では割愛いたします。

2-1. Unreal Insightsの使い方

Unreal Insightsは、Unreal Engineに標準で搭載されているプロファイリングシステムのことで、
データの収集、分析、および視覚化を行ってくれます。
今回はこのシステムを使用して、アプリのボトルネックを探しパフォーマンスを向上させていきます。

まずUnreal Insightsを開いていきます。
UEをダウンロードする際に同時にダウンロードされているので、下記画像のように
UE > Engine > Binaries > Win64 の中にある「UnrealInsights.exe」を開きます。

アプリケーションが開きますが、今は一旦このまま置いておきます。
(下の画像ではいくつかデータが入っていますが、初めて開いた場合はデータには何も入っていません。)

次にゲーム起動時にプロファイリング用のデータを作成する設定を行います。
UEのエディタの環境設定から、プレイ > 追加の起動パラメータ の欄に下記コマンドを追加します。

-trace=cpu,frame,bookmark,log -statnamedevents

このコマンドにより、スタンドアロンモードでゲームを開始した時、ログとしてプロファイリング用のデータがUnreal Insightsで参照可能な形式で作成されます。

スタンドアロンモードでゲームを起動中にUnreal Insightsを開くとデータが「LIVE」で作成されているのを確認できます。

起動後10秒くらいしたらゲームを終了し、作成されたデータをUnreal Insightsで確認します。

今回はゲームを起動するためのプロファイリングではなく、通常ゲーム時のプロファイリングを行いたいので、
下の画像右側の、安定し始めた部分のデータを調査します。

右側の部分をクローズアップした画像がこちらです。

上段のピンクと黄緑の帯でFEngineLoopと書かれている部分があります。
これが1フレームの処理になり、今回の場合だと180msほどかかってしまっています。
(60FPSのゲームを目指す場合は16.6ms以下を目指さないといけない)

次にもう少し細かく見ていきます。

1フレームの処理の中で時間のかかっていそうな処理を調べていきます。
このデータの見方は、上から順に処理が細かくなっていき、各処理の流れは右方向に進んでいきます。

何度もプロファイリングを行っている人なら、パッと見ただけでどこら辺がおかしいのかわかるのかもしれないですが、
自分はまだ初学者なので少しずつ解読します。

細分化されている下の方の処理で、明らかに長い時間がかかってしまっている処理が、紫色の帯のFunctionという部分になります。

1フレーム全体で180msほどかかっているのに対し、このFunctionで177.9msもの時間を使っているようなので、ほぼこの処理のせいで遅くなっているように見えます。
このFunctionの親を見てみるとBlueprint Timeと書かれた帯があるので、ここではBlueprintに問題があるのかな、くらいまでわかります。

Unreal Insightsは、処理全体の流れや、どこら辺で問題が起きているかなどを見るために使います。
しかし、Blueprintのひとつひとつまで詳しくみることはできないので、次は別のプロファイリングシステムを使ってもう少し深ぼっていきます。

2-2. Session Frontendの使い方

Session Frontendも、Unreal Insightsと同様、Unreal Engineに標準で搭載されているプロファイリングシステムのことで、
データの収集、分析、および視覚化を行ってくれます。
Unreal Insightとの違いは、Session Frontendの方が、より詳しくファンクションの中身を見ていくことができますが、一方でUnreal Insightsほど可視性がよくないです。
そのため、Unreal Insightsで大体の処理の流れと問題点のあたりをつけ、Session Frontendで詳しく見ていく流れが良いと思います。

Session Frontendを使うためにも専用のセッションデータが必要になるので、コマンドでデータを作成します。

UEのゲームを開始した上で、画面下部のアウトプットログタブから、コマンド入力欄に「stat startfile」と入力します。

コマンドを入力するとゲーム画面上にプロファイリング中の文字が表示されます。

こちらも同じく10秒ほど待機してから、コマンド入力欄に「stat stopfile」と入力します。

これでデータが取得できました。

画面上部のツールタブからセッションフロントエンドを選択し、画面を表示させます。

プロファイラのタブへ移動し、画面中央上部のファイルをロードボタンを押下します。

ファイル選択画面になるので、最新のフォルダを選択します。
(初めて行う際は一つしかフォルダが表示されないはずです。)

フォルダを選択するとプロファイラ画面にデータが表示されます。

今回使用するのは右下のイベント名と書かれた部分になります。
通常ゲーム時のプロファイリングを行いたい場合は、イベント名から「GameThread」を選択します。
その後は、Unreal Insightsで表示があった「FrameTime」を選択し、処理時間がかかっているタブをどんどん開いていきます。

今回の場合は、最終的にファンクション名まで行き着きました。

Function/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter.BP_ThirdPersonCharacter_C.MeanlessFunction

「BP_ThirdPersonCharacter」の「MeanlessFunction」というファンクションに時間がかかってしまっているようなので、検証します。

2-3. 遅延原因の特定方法

Unreal InsightsとSession Frontendを利用して、遅延の原因になっていそうなファンクションを特定したので、実際にUE画面から調査してみます。

BP_ThirdPersonCharacterのファイルを開き、Blueprintを確認します。

ファイル内に「Meanless Function」というファンクションが「イベントTick」につながっており、毎フレームごとに呼ばれてしまっていることがわかります。

ファンクションの内部に移動すると、文字通り、意味のない処理をFor文で2万回も行っていました。
1フレームごとに2万回、print stringをしていると考えると恐ろしいバグです。
(もちろん仕込みです。すみません。)

この「Meanless Function」を削除し、もう一度ゲームを開始してみます。

見事「Game」の数値が改善され「Frame」も60FPSに対応できる数値まで改善できました。

念の為Unreal InsightsやSession Frontendも確認しましたが、修正前と比べ異常な処理時間がかかっている部分がなくなっているのを確認できました。

おわりに

今回は、前回に引き続き、UE5を利用してプロファイリングの方法を説明してきました。
今回学習した、GPUで問題になっている箇所を探して対策を行い、その次にCPUで問題になっている箇所を探すフローは、
今後の実際の現場でも使っていけるのではないかと感じています。
もちろん実際のプロジェクトでは、より複雑に原因が入り組んでいることが想像できるので、
基礎の勉強をしつつ、実際のプロファイリングもどんどん行っていきたいです。

まだ前回の記事(プロファイリングのワークフローGPU編)をご覧になっていない方は、そちらも是非読んでいただけると嬉しいです。

現在ISIDはweb3領域のグループ横断組織を立ち上げ、Web3およびメタバース領域のR&Dを行っております(カテゴリー「3DCG」の記事はこちら)。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください!
私たちと同じチームで働いてくれる仲間を、是非お待ちしております!
ISID採用ページ(Web3/メタバース/AI)

参考

執筆:@okazaki.wataru、レビュー:@yamada.y
Shodoで執筆されました