[4.20版] UE4におけるLoadingとGCのProfilingと最適化手法

65.4K Views

August 02, 18

スライド概要

以前出した「 UE4におけるLoadingとGCのProfilingと最適化手法」の4.20版です。
元スライドのURL: https://www.slideshare.net/EpicGamesJapan/ue4loadinggcprofiling/

4.20からGCのSweep処理がが複数フレームで分散可能になったので、そちらを追記いたしました。


(UE4でローディング時間やGCによるカクつき(ヒッチ)を軽減したい場合に、どの様に問題部分を特定し、またどの様に最適化するかのはじめの一歩をまとめました。ご参考になれば幸いです。)

profile-image

Unreal Engineを開発・提供しているエピック ゲームズ ジャパンによる公式アカウントです。 勉強会や配信などで行った講演資料を公開しています。 公式サイトはこちら https://www.unrealengine.com/ja/

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

UE4におけるloadingとGCの Profilingと最適化手法 Epic Games Japan / Support Manager Nori Shinoyama 4.20 version!!

2.

4.20からの変更点 • 数ページのスライドが変更されておりますが、内容は9割一緒です。 • 変更のない箇所 – Load時間のProfiling / Optimization • 変更のある箇所 – GCのProfilingとOptimizationの箇所 • 具体的にはP31~P35のみです。 • 変更のある場所には、右の様に強調します。 4.20で更新

3.

アジェンダ 1. Load時間(及びパッケージサイズ)のProfilingとOptimization 2. GCのProfilingとOptimization

4.

アジェンダ 1. Load時間(及びパッケージサイズ)のProfilingとOptimization 2. GCのProfilingとOptimization

5.

LOAD時間の PROFILINGとOPTIMIZATION

6.

Loadからシーンに出るまでのフロー Storage Memory (NAND Flush Memory) 1. メモリにロード 一般的に「ロード時間」 と呼ばれる部分 2. AddToWorldして Visibleに AddToWorld時の瞬間的なカクつき (Level Streaming時にプレイに影響)

7.

Load時間のProfilingと最適化手法 • Profiling – Stat Levels – Loadtimes.dumpreport – 注意点: ENABLE_LOADTIME_TRACKING defineに関して – PERF_TRACK_DETAILED_ASYNC_STATS • Optimization – おさらい – FileOpenOrder – 不要なデータの削除例 • • – – – Material: Shader Permutation Reduction Vertex: Reverse Index Bufferの削除 AssetManagerによるPreloading AddToWorldをフレーム分散させる EventBeginPlayの負荷を減らす

8.

Load時間のProfiling Stat Levels • • • • • ロード時間計測用のStat 灰=パーシスタントレベル 赤=未ロード 紫=ロード中 橙=AddToWorld 緑=ロード完了

9.

Load時間のProfiling Loadtimes.DumpReport (Loadtimes.reset) • LoadTimes.DumpReport – 各種パッケージがのロード時間をリスト • • FILEとつけると.loadreportファイルにダンプ LOWTIME=0.05コマンドつけると、0.05s以下のパッケージはダンプされない。 Dumping all loaded assets by exclusive load time: 2158.6ms: /Game/Product/Assets/Maps/AAAAAAAAA 1201.3ms: /Game/Product/Assets/Maps/BBBBBBBBB 635.9ms: /Game/Product/Assets/Maps/CCCCCC 598.0ms: /Game/Product/Resources/Environments/StaticMeshAAAA 525.3ms: /Game/Product/Resources/Environments/StaticMeshBBBB Dumptimes出力例

10.

Load時間のProfiling Loadtimes.reset • LoadTimes.Reset – このコマンドが計測しているロード時間などをリセットできる。 計測したいロード部分の前に、これを呼び出しておくこと。

11.

Load時間のProfiling (備考) 整備されていないコマンドやDefineについて • ロード時間を調査したりコードを読むと、以下のコマンドやdefine を見つけるかと思います。 – – – – LoadTimes.DumpTracking LoadTimes.DumpTrackingLow #define ENABLE_LOADTIME_TRACKING #define ENABLE_LOADTIME_RAW_TIMINGS • これらは整備されておらず、使用しても有益な情報は得られません。 今後どうなるかはわかりませんが、現状は使わないでください

12.

AddToWorldのProfiling PERF_TRACK_DETAILED_ASYNC_STATS (AsyncLoading.h) • LevelのShouldBeVisibleがONになったりなどのAddToWorldをす る際にどれだけのコストがかかるかがでる。 例: UWorld::AddToWorld: updating components for /Game/Sub took (less than) 110.62 ms Detailed AddToWorld stats for '/Game/Sub' - Total 425.37ms Move Actors : 0.00 ms Shift Actors : 0.00 ms Update Components : 425.12 ms Init BSP Phys : 0.00 ms Init Actor Phys : 0.00 ms Init Actors : 0.00 ms Initialize : 0.08 ms …

13.

Load時間のProfilingと最適化手法 • Profiling – Stat Levels – Loadtimes.dumpreport – 注意点: ENABLE_LOADTIME_TRACKING defineに関して – PERF_TRACK_DETAILED_ASYNC_STATS • Optimization – おさらい – FileOpenOrder – 不要なデータの削除例 • • – – – Material: Shader Permutation Reduction Vertex: Reverse Index Bufferの削除 AssetManagerによるPreloading AddToWorldをフレーム分散させる EventBeginPlayの負荷を減らす

14.

Load時間のOptimization おさらい – PAK ファイルを使う – Compressionはコンソール次第 公式ドキュメント.pakファイルの圧縮を参考にしてください

15.

Load時間のOptimization FileOpenOrder • ゲーム内で読み込み順に、Pakファイル内部のアセットをソートする機能 – 事前にプレイして、アセット読み込み順のログを取る必要がある – やり方の詳細はOfficial Docにあります プロジェクトのパッケージ化 • ※HDDのシーク距離を抑えるためのものです。

16.

Load時間のProfilingと最適化手法 • Profiling – Stat Levels – Loadtimes.dumpreport – 注意点: ENABLE_LOADTIME_TRACKING defineに関して – PERF_TRACK_DETAILED_ASYNC_STATS • Optimization – おさらい – FileOpenOrder – 不要なデータの削除例 • • – – Material: Shader Permutation Reduction Vertex: Reverse Index Bufferの削除 AssetManagerによるPreloading EventBeginPlayの負荷を減らす

17.

Load時間のOptimization 不要なデータの削除: Shader Permutation Reduction • UE4.13から搭載された機能。 – プロジェクトで使わない処理を前もって指定することで、その処理用のシェー ダを生成しない。 • マテリアルサイズ&シェーダコンパイルの時間どちらにも効果がある。

18.

このマテリアルで検証。 (usageはstatic lightingのみ。)

19.

25 17 全部つけたもの vs 全部消したもの シェーダ数を32%削減

20.

Load時間のOptimization 不要なデータの削除: Shader Permutation Reduction • その他、Materialのデータ量削減に関しては、以下のドキュメント で詳細を述べていますので、参考になさってください。 マテリアルとマテリアルインスタンスの仕組みと問題点の共有

21.

Load時間のOptimization 不要なデータの削除: 不要Index Bufferらの削除 基本的にOffで良いと思うもの Reverse Index Buffer マイナススケールレンダリング時にコンテキストロールを削減するための機能 こちらは要検討 Depth-only Index Buffer 影描画時に使うバッファ。 こちらはOffにすることでShadowMap作成のGPUコストが若干上がる可能性あり Adjacency Index Buffer Tessellationのためのバッファ。使わない場合はOffに。 (Tessellation未対応のプラットフォームではこのチェックの有無に関わらずこの バッファは生成されません)

22.

Load時間のOptimization AssetManagerによるPreloading Level単位ではなくAsset単位で裏で読み込む機能があります。 UE4 Doc: アセット管理 【UE4】AssetManagerを使用したレベルストリームの高速化 メモリが許すかつロードできるアセットが事前にわかる場合、 前読みも検討できるかと思います。

23.

AddToWorldの最適化 AddToWorldの処理を複数フレームに分散させる • Project SettingsにLevel Streaming In(Out)時の AddToWorld(RemoveFromWorld)を分散させる設定があります。 AddToWorld時の 1フレームの処理負荷制限 投入されるオブジェクトの単位 RemoveFromWorldの 1フレームの処理負荷制限 投入されるオブジェクトの単位

24.

AddToWorldの最適化 EventBeginPlayの処理を減らす • BeginPlayに高負荷な処理をしているとAddToWorldに影響 • ConstructionScriptに逃がせるものは逃がすなど

25.

Load時間のProfilingと最適化手法 • Profiling – Stat Levels – Loadtimes.dumpreport – 注意点: ENABLE_LOADTIME_TRACKING defineに関して – PERF_TRACK_DETAILED_ASYNC_STATS • Optimization – おさらい – FileOpenOrder – 不要なデータの削除例 • • – – Material: Shader Permutation Reduction Vertex: Reverse Index Bufferの削除 AssetManagerによるPreloading EventBeginPlayの負荷を減らす

26.

アジェンダ 1. Load時間(及びパッケージサイズ)のProfilingとOptimization 2. GCのProfilingとOptimization

27.

GARBAGE COLLECTIONの PROFILINGとOPTIMIZATION

28.

Garbage CollectionのProfilingと最適化手法 • • • • GCの問題点 GCの考え方 GCコストの計測方法 タイトルでやることできること

29.

GCの問題点 • GCが起きたときの急激なヒッチ – レベルストリーミングにより。。 • 遷移時のシーンのオブジェクトの一時的な増加 • 遷移後の大量のオブジェクトの破棄

30.

GCの考え方 UObjectArray: UObject(Uproperty)の一次元配列 UE4はUobjectすべてを一次元配列で管理 GCが起きると、このすべてのオブジェクトを検索し、不要なものを削除する つまり、 GCのコスト = 検索コスト + 削除コスト

31.

削除コストの詳細 GCのフレーム分散 4.20以前 GCのコスト = 検索コスト + 削除コスト と記載しましたが、正確ではありません 削除コスト = 依存関係の切断 + 実際のオブジェクトの削除 こちらはフレーム分散可能 GCが呼び出されたフレームのインパクト = 検索コスト + 依存関係の切断 今日はこれを便宜上「削除コスト」と呼びます

32.

削除コストの詳細 GCのフレーム分散 4.20で更新 GCのコスト = 検索コスト + 削除コスト と記載しましたが、正確ではありません 削除コスト = 依存関係の切断 + 実際のオブジェクトの削除 どちらも分散可能に!! GCが呼び出されたフレームのインパクト = 検索コスト + 依存関係の切断の1フレーム目 今日はこれを便宜上「削除コスト」と呼びます

33.

4.20以前 つまり、 GCのコスト = 検索コスト + 削除コスト

34.

4.20で更新 つまり、 GCのコスト = 検索コスト + 削除コスト 複数フレームに分散されるのでカクツキの 原因にはなりにくくなるはずです。

35.

4.20からGCによるSweep処理のBeginDestroyが分散可能に (defaultでON) 4.20で更新

36.

エンジン最適化による高速化 • エンジン内部の最適化で4.16で高速化が実現 • 例: – 検索: 40ms -> 12ms – 削除: 70ms -> 48ms

37.

(発展)レベルストリーミングアウトのときの FinishDestroy(FD)のフレーム分散化 現在、レベルストリーミングアウトのとき、実際のオブジェクトの削除部分がフレームをまたいで分 散される様な設定になってはおりません。 それをするためには以下の様に修正をお願いいたします。 (UE4としてこれはデフォルト動作としないため、今後組み込まれる予定はございません。 しかし、下の修正での動作実績があり、Epic本社もこの変更を認めております。) ---■レベルストリーミングアウト時の、FinishDestroyのインクリメンタル化 1. void UWorld::UpdateLevelStreaming()のForceGarbageCollection(true); <- 中身をfalseに。 2.UWorld* UWorld::FindWorldInPackage()のGetObjectsWithOuterを GetObjectsWithOuter(Package, PotentialWorlds, false, EObjectFlags::RF_NoFlags, EInternalObjectFlags::PendingKill); に。 3. UWorld* UWorld::FollowWorldRedirectorInPackage()のGetObjectsWithOuterを GetObjectsWithOuter(Package, PotentialRedirectors, false, EObjectFlags::RF_NoFlags, EInternalObjectFlags::PendingKill);に。

38.

GCコストの確認方法

39.

GC関連コストの確認方法 1. 2. 3. 4. 5. Log loggarbage log (Log loggarbage verbose) Stat dumphitches (Stat Startfile/Stopfile) CBD Profiling Tools Obj –list / Bluerprint Stats

40.

1/5: LogGarbage log • GC負荷の大まかなプロファイルのメインコマンド • “log LogGarbage log”コマンドでGC時に以下のようなログが出る。 検索コスト LogGarbage: 74.040701 ms for GC LogGarbage: 2.808510 ms for unhashing unreachable objects. Clusters removed: 0. 削除コスト

41.

2/5: ヒッチ時のCPU負荷のロギング Command: “stat dumphitches” ----------------- Game Thread 325.24ms 325.216ms ( 4) - Thread_189a1_0 - GameThread - STATGROUP_Threads 325.208ms ( 2) - FrameTime - STAT_FrameTime - STATGROUP_Engine 322.049ms ( 1) - GameEngine Tick - STAT_GameEngineTick 316.684ms ( 1) - World Tick Time - STAT_WorldTickTime 293.497ms ( 1) - CollectGarbageInternal 145.057ms ( 1) - FRealtimeGC::PerformReachabilityAnalysis -

42.

注意点: VERIFY GC ASSUMPTIONS • Development Buildで基本ON (Test/Shippingでは基本無効) • CollectGarbageInternal.VerifyGCAssumptionsが走ってGCが激重に • なしにするには、実行時に”-NOVERIFYGC”オプションをつけて起動 GCの本来のコストを見積もるならば、 TESTビルドで検証することを強くおすすめします。

43.

3/5: Stat startfile/stopfile

44.

4/5: 削除コスト調査用Define # define PROFILE_GCConditionalBeginDestroy # define PROFILE_GCConditionalBeginDestroy_byClass LogGarbage: Collecting garbage LogGarbage: 9.762678 ms for GC LogGarbage: 59.374099 ms for unhashing unreachable objects. Clusters removed: 111. Items 18567 Cluster Items 735 LogTemp: 1090 cnt 2.23us per 2.43ms total /Game/Blueprints/Character/AAAAAAAAA LogTemp: 615 cnt 2.58us per 1.59ms total /Game/Blueprints/Character/BBBBBBBBBB LogTemp: 698 cnt 2.11us per 1.48ms total /Game/Blueprints/Character/CCCCCCCCCC LogTemp: 489 cnt 2.64us per 1.29ms total /Game/Blueprints/Gimmick/GimmickAAAAA LogTemp: 261 cnt 4.22us per 1.10ms total /Game/Maps/MAPMAPMAP

45.

5/5 シーン内にどんなUobjectがあるかを調査するコマンド Obj list Class Count MetaData 357 SkeletalMesh 1 Package 575 Class 2393 FontFace 6 BoolProperty 4797 FloatProperty 3788 ObjectProperty 3251

46.

5/5 シーン内にどんなUobjectがあるかを調査するコマンド Bluerprint Stats Plugin Total,DOBP, NumNodes, …,PureTotal,UserFnCount,UserMacroCount,NumSubobjects,NumPropertyObjects 1268,338,105592,18571,2893,2341,19076,15812,2099,305,669,193895,85528

47.

タイトルでできること

48.

タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。) <-最初に言及

49.

GCのためのレベル分割 • レベルストリームで大きく読み込むと、 一時的にシーン全体のオブジェクトが増え、検索コストが増加する。 • レベルストリームで大きく捨てると、削除コストが増加する。 • GCが小さくなるように、サブレベルを更に細かく分割し、 細かく読み込み、細かく捨てる。 • 最後の最後の手だが、この方針で行くならば、 早めに検証しサイズを見積もらなければいけない。

50.

タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。)

51.

DisregardGCObject UObjectArray: UObject(Uproperty)の一次元配列 ゲーム常駐部分 Concept • ゲーム起動時から常にメモリにいて欲しいオブジェクトは必ずある。 絶対にGCしてほしくないので、そもそもそれらをGC対象から外す。 • ゲーム起動時(PreInit())に読み込むものをGC対象外とする。

52.

DisregardGCObject Maximum Object Count Not Considered By GC Sizeo Of Permanent Object Pool この2つの数値を設定します。

53.

DisregardGCObjectの設定方法 1. DisregardGCObjectの有効化 [/Script/Engine.GarbageCollectionSettings] gc.MaxObjectsNotConsideredByGC=1 gc.SizeOfPermanentObjectPool=0 0以外の数にすることで有効になる 2. 一回起動するとログにこのような数字が出る。 LogUObjectArray: 52083 objects as part of root set at end of initial load. LogUObjectAllocator: 9937152 out of 0 bytes used by permanent object pool. 上が、PreInitでロードされたUObjectsの総数 下が、PreInitでロードされたUObjectsの総バイト数 3. この数値を、先程の値に再設定 [/Script/Engine.GarbageCollectionSettings] gc.MaxObjectsNotConsideredByGC= 52083 gc.SizeOfPermanentObjectPool= 9937152

54.

DisregardGCObject: 成果 設定前 LogGarbage: 74.040701 ms for GC LogGarbage: 2.808510 ms for unhashing unreachable objects. 20%ほど改善 設定後 LogGarbage: 60.583722 ms for GC LogGarbage: 2.114550 ms for unhashing unreachable objects.

55.

タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。)

56.

効率的なクラスタの模索 Concept: 複数のUobjectをクラスタ化して、検索コストを減らす。 ※本件は削除コストには効果がありません。 上記四項目、 ActorもClusteringに含めるか、BlueprintもClusteringに含めるか。。

57.

効率的なクラスタの模索 Actorの中にCanBeInCluster設定項目がありま す。 これで、クラスターの中にそのアクターを含め るかどうかを設定できます。 ※DefaultではStaticMeshActorはDefaultで True。

58.

効率的なクラスタの模索: 注意点 Clusteringされるタイミング クラスタが作成されるのは、ロードされ、 AddToWorldされるタイミングです。 その後、新たな参照が貼られる様なオブジェク トは、CanBeInClusterをOffにしなければいけ ません。 Matinee/SequencerでStaticMeshActorを動 かすときなどは注意。 (Developmentビルドならば、エラーを出して クラッシュするので調査は簡単です。)

59.

Blueprint Clustering Blueprintは内部に沢山のUObjectを保持しています。 なので、ブループリント単位でのクラスタリングを行なうと、検索コストをへら すことが可能です。 と言いつつ、このオプションでクラッシュが発生するとの報告があります。 それらが、回避可能か、エンジンとして修正可能かの情報を集めています。 もしも、クラッシュした場合、UDNにてご報告頂ければ幸いです。

60.

効率的なクラスタの模索: 効果 Actor Clustering Garbage collection (StaticMeshActor is NOT in GC clusters): 25.840614 ms for GC Garbage collection (StaticMeshActor is in GC clusters): 14.977702 ms for GC ------------------------Blueprint Clustering Garbage collection (BlueprintGeneratedClass does NOT create GC clusters): 42.674898 ms for GC Garbage collection (BlueprintGeneratedClass creates GC clusters): 33.523061 ms for GC

61.

タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。)

62.

Uobject 削減の模索 UObjectArray: UObject(Uproperty)の一次元配列 UObjectが多ければ必ずヒッチが生じる Uobject自身を不必要に増やさない対策

63.

Macro -> FunctionによるUPropertyの削減 Concept: マクロの中にマクロが含まれている -> 最終的に展開されたBPは非常に巨大なものに 例)あるBPから一つのマクロを取り除く -> プロパティー数が1066 -> 600まで減る マクロは、中にlatentノードがなければ関数化によって取り除くことができます。 上記手法によりあるBPを関数化した場合、 とあるマップで読み込んでいるキャラクター関連のUObjectが 20781から5486へと75%のプロパティ数の削減を実現しました

64.

Macro -> FunctionによるUPropertyの削減: 確認方法 Bluepirnt Stats Plugin Blueprint stats for 1268 blueprints in XXXXXXXXXXX Total,DOBP, NumNodes, …,PureTotal,UserFnCount,UserMacroCount,NumSubobjects,NumPropertyObjects 1268,338,105592,18571,2893,2341,19076,15812,2099,305,669,193895,8552 8 LogBlueprintStats: LogBlueprintStats: LogBlueprintStats: LogBlueprintStats: LogBlueprintStats: LogBlueprintStats: LogBlueprintStats: LogBlueprintStats: ------------------------------------------MacroName,NumFlatInstances,NumProperties ForEachLoop,850,954 ForEachLoopWithBreak,225,396 PlayTalkEndAnimation,319,294 PlayerMoveTo,12,144 CS Character Setting,0,116 End Cutscene Matinee,9,99 マクロに特化した情報が出るように 拡張されたコードがあり。。 (4.17で正式採用予定) 欲しい人はご連絡ください。

65.

タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。)

66.

Blueprint NativizationによるUobjectの削減 BP Officeでテスト 非Native: Total 31359 Objects Native: Total 30885 Objects 差: 475 Objects ---主に減っている箇所 StructProperty 4901 FloatProperty 3450 ObjectProperty 2595 IntProperty 1528 BoolProperty 3653 4756 3356 2569 1482 3617 BP Office Sample Native化対象のBP

67.

タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。)

68.

アジェンダ 1. Load時間(及びパッケージサイズ)のProfilingとOptimization 2. GCのProfilingとOptimization

69.

まとめ • コンソール開発は、 常にパフォーマンスを意識して開発する必要があります • なるべく早期から、実機で、定期的な パフォーマンス検証を行ってください • UE4は調整、検証を行える様に様々なツールを用意しております • 疑問点やヘルプは、 UDNもしくは弊社サポートまでお問い合わせください