[UE4] CSV Profileを用いたパフォーマンス計測


#1. はじめに
[検証バージョン：4.22.1]
　4.17から追加されたCSVProfile機能[(CSV Profiler)](https://docs.unrealengine.com/en-US/Engine/Performance/CSVProfiler/index.html)を利用すると簡単にGame/Render/RHI ThreadやTick/FileIOのパフォーマンスを計測することができます。この機能を利用することで.csvファイルとしてパフォーマンス情報を取得し、以下のようなグラフを作成することも可能です。


<img width="400" alt="2019-05-19_11h48_27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/fe801d8a-9633-54f7-6135-495b787901de.png"><img width="400" alt="2019-05-19_11h48_33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/3d326598-41b2-ea53-74ff-a5dcdb33bde8.png">
![2019-05-19_21h17_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/be89a5e0-935e-c1d0-fccb-7c2ecb0993ab.png)

#2. 使い方
　パフォーマンス計測結果を.csvファイルとして出力する方法は以下の2つの方法です。

　**・コンソールコマンドを入力する(CsvProfile Start/CsvProfile Stop)**
　**・起動引数として渡す(-csvCaptureFrames=[Frame])**

　出力された結果は[Project]/Saved/Profiling/CSVの配下に出力されます。そして、この機能はDebug/Development/Testビルドにおいて利用可能です。
![2019-05-19_09h49_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/db28ab60-20ed-f765-3833-2d9f760e9b88.png)

.csvの中身は以下のようになっているためグラフ化すれば最初の図のようなグラフが取得できます。

![2019-05-21_11h05_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/a093a3a0-fe88-2446-e0f5-477220fcf674.png)


#3. コマンド・機能一覧
　この機能を利用するためのコンソールコマンドや、プロファイル方法を調整するためのコンソール変数は以下の通りです。

##Console Command

| コマンド				|機能		|
|:--------------			|:---		|
| CsvProfile Start			|キャプチャ開始<br>キャプチャを開始します|
| CsvProfile Stop			|キャプチャ停止<br>キャプチャを終了します|
| CsvProfile Startfile=[Filename]	|出力ファイル名指定<br>指定の名前で.csvファイルを保存します|
| CsvProfile ExitonCompletion		|アプリケーション終了指定<br>キャプチャ終了後にアプリケーションを終了します|
| CsvProfile Frame=[Num]		|キャプチャフレーム数指定<br>指定のフレーム数だけキャプチャを行います|
| CsvProfile Repeat=[Count]		|繰り返し回数指定<br>指定の回数だけキャプチャを実施します。<br>-Frame=100 -Repeat=5のよう複合も可能です|

##Console Variable

| コマンド				|機能		|
|:--------------			|:---		|
| csv.BlockOnCaptureEnd			|CSV書き込み時ブロック設定<br>0:ファイル書き込み時にGameThreadブロック無効<br>1:ファイル書き込み完了までGameThreadをブロック		|
| csv.ContinuousWrites			|CSV出力方法設定<br>0:メモリに蓄積しキャプチャ終了時に書き込み<br>1:随時書き込み	|
| csv.Shipping.ContinuousWrites		|CSV出力方法設定 (Shippingのみ)<br>設定内容はcsv.ContinuousWritesと同じ|
| csv.CompressionMode			|出力時のCSV圧縮設定<br>-1:選択時圧縮<br>0:.csv出力(強制圧縮無効)<br>1:.csv.gzで書き込み(強制圧縮有効)		|
| csv.statCounts			|Statsカウント出力設定<br>0:出力なし<br>1:出力あり		|
| csv.WriteBufferSize			|CSV書き込みバッファサイズ<br>GZip圧縮時は0以外を指定	|
| r.GPUCsvStatsEnabled			|GPU情報CSV出力指定	|


##Launch Argument

| コマンド			|機能		|
|:--------------		|:---		|
| csvGpuStats			|GPU情報CSV出力指定<br>r.GPUCsvStatsEnabledの値に応じてGPU情報出力を指定できます|
| csvTest			|GameThreadのCSVテスト出力します|
| csvTestMT			|GameThreadおよびRenderThreadのCSVテスト出力します|
| csvCategories=[Category]	|特定のカテゴリのみを出力します<br>各カテゴリはCSV_DEFINE_CATEGORY()で定義されています|
| csvNoProcessingThread		|CSVプロファイル専用のスレッドを生成しません|
| csvStatCounts			|同フレーム内にキャプチャした回数をデータとして出力します<br>csv.statCountsと同じです|
| csvCaptureFrames=[Frame]	|指定のフレーム数分キャプチャを実施します<br>CsvProfile Frameと同じです|
| csvRepeat=[Count]		|指定の回数分キャプチャを実施します<br>CsvProfile Repeatと同じです|
| csvCompression=[Mode]		|出力時のCSV圧縮モードを指定します<br>csv.CompressionModeと同じです|
| ExitAfterCsvProfiling		|CSVプロファイル終了後にアプリケーションを終了します。<br>CsvProfile ExitonCompletion と同じです|

#4. 応用
##4.1. Shippingでの利用方法
 CSVプロファイルをShippingビルドで有効にするためには以下のように各defineを追加する必要があります。Shippingで利用できるようになることはPGOなどを適用する場合などの計測時に有効です。

```cpp:CsvProfiler.h
#define CSV_PROFILER_ENABLE_IN_SHIPPING 1
#define CSV_PROFILER_ALLOW_DEBUG_FEATURES 1

```

将来的にはProjectのTarget.csファイルで次の定義を追加することで切り替えができるようになると思われます。

```cs:[Project].Target.cs
        GlobalDefinitions.Add("CSV_PROFILER_ENABLE_IN_SHIPPING = 1");
        GlobalDefinitions.Add("CSV_PROFILER_ALLOW_DEBUG_FEATURES = 1");
```

Windowsの場合、Shippingのプロファイル結果は以下のパスに出力されます。
C:\Users\[USER_NAME]\AppData\Local\[PROJECT]\Saved\Profiling\CSV

##4.2. 出力されるプロファイル情報

　[CSV Profiler](https://docs.unrealengine.com/en-US/Engine/Performance/CSVProfiler/index.html)の「Adding Stats」項で記載されているように、"-stats MyCategory/"のような記述をすることで、特定のstatsのみをキャプチャすることもできます。CSV Profilerでは多岐にわたる項目をキャプチャするため、不要な項目を除外したり、特定の項目のみをピックアップする際に役に立ちます。
　このStatsは**CSV_CUSTOM_STAT()**および、**CSV_CUSTOM_STAT_GLOBAL()**マクロで定義されたものが対象となっています。

<details><summary>Profile項目一覧</summary><div>


|#|項目|
|:---|:---|
|1|Exclusive/RenderThread/RenderOther|
|2|Exclusive/RenderThread/InitViews_Scene|
|3|Exclusive/RenderThread/FetchVisibilityForPrimitives|
|4|Exclusive/RenderThread/InitViews_Shadows|
|5|Exclusive/RenderThread/RenderPrePass|
|6|Exclusive/RenderThread/ComputeLightGrid|
|7|Exclusive/RenderThread/FRDGBuilder_Execute|
|8|Exclusive/RenderThread/RenderShadows|
|9|Exclusive/RenderThread/RenderBasePass|
|10|Exclusive/RenderThread/CustomDepthPass|
|11|Exclusive/RenderThread/RenderCustomDepthPass|
|12|Exclusive/RenderThread/RenderVelocities|
|13|Exclusive/RenderThread/AfterBasePass|
|14|Exclusive/RenderThread/RenderLighting|
|15|Exclusive/RenderThread/RenderFog|
|16|Exclusive/RenderThread/RenderOpaqueFX|
|17|Exclusive/RenderThread/RenderTranslucency|
|18|Exclusive/RenderThread/RenderPostProcessing|
|19|RHI/DrawCalls|
|20|RHI/PrimitivesDrawn|
|21|Exclusive/GameThread/Input|
|22|Exclusive/GameThread/EngineTickMisc|
|23|Exclusive/GameThread/AsyncLoading|
|24|Exclusive/GameThread/WorldTickMisc|
|25|Exclusive/GameThread/NetworkIncoming|
|26|Exclusive/GameThread/NavigationBuild|
|27|Exclusive/GameThread/QueueTicks|
|28|Exclusive/GameThread/Effects|
|29|Exclusive/GameThread/PlayerControllerTick|
|30|Exclusive/GameThread/CharacterMovement|
|31|Exclusive/GameThread/Animation|
|32|Exclusive/GameThread/Physics|
|33|Exclusive/GameThread/EventWait/EndPhysics|
|34|Exclusive/GameThread/TimerManager|
|35|Exclusive/GameThread/Tickables|
|36|Exclusive/GameThread/AIPerception|
|37|Exclusive/GameThread/EnvQueryManager|
|38|Exclusive/GameThread/Camera|
|39|Exclusive/GameThread/GarbageCollection|
|40|Exclusive/GameThread/RecordTickCountsToCSV|
|41|Exclusive/GameThread/RecordActorCountsToCSV|
|42|Exclusive/GameThread/EndOfFrameUpdates|
|43|Exclusive/GameThread/ViewportMisc|
|44|Exclusive/GameThread/UpdateLevelStreaming|
|45|Exclusive/GameThread/UI|
|46|Exclusive/GameThread/DebugHUD|
|47|Exclusive/GameThread/RenderAssetStreaming|
|48|Exclusive/GameThread/EventWait/RenderAssetStreaming|
|49|Exclusive/GameThread/EventWait|
|50|Exclusive/GameThread/CsvProfiler|
|51|CPUUsage_Process|
|52|CPUUsage_Idle|
|53|FileIO/PakPrecacherRequests|
|54|FileIO/EDLEventQueueDepth|
|55|FileIO/QueuedPackagesQueueDepth|
|56|FileIO/ExistingQueuedPackagesQueueDepth|
|57|Basic/TicksQueued|
|58|Ticks/SkeletalMeshComponent|
|59|Ticks/Character|
|60|Ticks/CharacterMovementComponent|
|61|Ticks/PlayerController|
|62|Ticks/AtmosphericFog|
|63|Ticks/HUD|
|64|Ticks/LineBatchComponent|
|65|Ticks/SpringArmComponent|
|66|Ticks/AbstractNavData|
|67|Ticks/GameplayDebuggerPlayerManager|
|68|Ticks/ParticleSystemManager|
|69|Ticks/StartPhysicsTick|
|70|Ticks/EndPhysicsTick|
|71|ActorCount/TotalActorCount|
|72|View/PosX|
|73|View/PosY|
|74|View/PosZ|
|75|View/ForwardX|
|76|View/ForwardY|
|77|View/ForwardZ|
|78|View/UpX|
|79|View/UpY|
|80|View/UpZ|
|81|View/Speed|
|82|TextureStreaming/StreamingPool|
|83|TextureStreaming/SafetyPool|
|84|TextureStreaming/TemporaryPool|
|85|TextureStreaming/CachedMips|
|86|TextureStreaming/WantedMips|
|87|FrameTime|
|88|MemoryFreeMB|
|89|PhysicalUsedMB|
|90|VirtualUsedMB|
|91|RenderThreadTime|
|92|GameThreadTime|
|93|GPUTime|
|94|CsvProfiler/NumTimestampsProcessed|
|95|CsvProfiler/NumCustomStatsProcessed|
|96|CsvProfiler/NumEventsProcessed|
|97|CsvProfiler/ProcessCSVStats|
|98|Exclusive/GameThread/EventWait/EndOfFrameUpdates|
|99|Exclusive/GameThread/EventWait/PostPhysics|
|100|FMsgLogf/FMsgLogfCount|


</div></details>


#5. まとめ
　手軽にプロファイルをとることができて計測コストも比較的軽量なため、自動テストやエージングテストなど長期的なプロファイルなどを行う際に役に立ちます。是非ご活用ください。