Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

UE4におけるLoadingとGCのProfilingと最適化手法

6,906 views

Published on

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

Published in: Engineering

UE4におけるLoadingとGCのProfilingと最適化手法

  1. 1. UE4におけるLoadingとGCの Profilingと最適化手法 Epic Games Japan / Support Manager Nori Shinoyama
  2. 2. アジェンダ 1. Load時間(及びパッケージサイズ)のProfilingとOptimization 2. GCのProfilingとOptimization
  3. 3. アジェンダ 1. Load時間(及びパッケージサイズ)のProfilingとOptimization 2. GCのProfilingとOptimization
  4. 4. LOAD時間の PROFILINGとOPTIMIZATION
  5. 5. Loadからシーンに出るまでのフロー Storage (NAND Flush Memory) Memory 1. メモリにロード 一般的に「ロード時間」 と呼ばれる部分 2. AddToWorldして Visibleに AddToWorld時の瞬間的なカクつき (Level Streaming時にプレイに影響)
  6. 6. 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の負荷を減らす
  7. 7. Load時間のProfiling Stat Levels • 灰=パーシスタントレベル • 赤=未ロード • 紫=ロード中 • 橙=AddToWorld • 緑=ロード完了 ロード時間計測用のStat
  8. 8. 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出力例
  9. 9. Load時間のProfiling Loadtimes.reset • LoadTimes.Reset – このコマンドが計測しているロード時間などをリセットできる。 計測したいロード部分の前に、これを呼び出しておくこと。
  10. 10. Load時間のProfiling (備考) 整備されていないコマンドやDefineについて • ロード時間を調査したりコードを読むと、以下のコマンドやdefine を見つけるかと思います。 – LoadTimes.DumpTracking – LoadTimes.DumpTrackingLow – #define ENABLE_LOADTIME_TRACKING – #define ENABLE_LOADTIME_RAW_TIMINGS • これらは整備されておらず、使用しても有益な情報は得られません。 今後どうなるかはわかりませんが、現状は使わないでください
  11. 11. 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 …
  12. 12. 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の負荷を減らす
  13. 13. Load時間のOptimization おさらい – PAK ファイルを使う – Compressionはコンソール次第 公式ドキュメント.pakファイルの圧縮を参考にしてください
  14. 14. Load時間のOptimization FileOpenOrder • ゲーム内で読み込み順に、Pakファイル内部のアセットをソートする機能 – 事前にプレイして、アセット読み込み順のログを取る必要がある – やり方の詳細はOfficial Docにあります プロジェクトのパッケージ化 • ※HDDのシーク距離を抑えるためのものです。
  15. 15. 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の負荷を減らす
  16. 16. Load時間のOptimization 不要なデータの削除: Shader Permutation Reduction • UE4.13から搭載された機能。 – プロジェクトで使わない処理を前もって指定することで、その処理用のシェー ダを生成しない。 • マテリアルサイズ&シェーダコンパイルの時間どちらにも効果がある。
  17. 17. このマテリアルで検証。 (usageはstatic lightingのみ。)
  18. 18. 全部つけたもの vs 全部消したもの 25 17 シェーダ数を32%削減
  19. 19. Load時間のOptimization 不要なデータの削除: Shader Permutation Reduction • その他、Materialのデータ量削減に関しては、以下のドキュメント で詳細を述べていますので、参考になさってください。 マテリアルとマテリアルインスタンスの仕組みと問題点の共有
  20. 20. Load時間のOptimization 不要なデータの削除: 不要Index Bufferらの削除 基本的にOffで良いと思うもの Reverse Index Buffer マイナススケールレンダリング時にコンテキストロールを削減するための機能 こちらは要検討 Depth-only Index Buffer 影描画時に使うバッファ。 こちらはOffにすることでShadowMap作成のGPUコストが若干上がる可能性あり Adjacency Index Buffer Tessellationのためのバッファ。使わない場合はOffに。 (Tessellation未対応のプラットフォームではこのチェックの有無に関わらずこの バッファは生成されません)
  21. 21. Load時間のOptimization AssetManagerによるPreloading Level単位ではなくAsset単位で裏で読み込む機能があります。 【UE4】AssetManagerを使用したレベルストリームの高速化 UE4 Doc: アセット管理 メモリが許すかつロードできるアセットが事前にわかる場合、 前読みも検討できるかと思います。
  22. 22. AddToWorldの最適化 AddToWorldの処理を複数フレームに分散させる • Project SettingsにLevel Streaming In(Out)時の AddToWorld(RemoveFromWorld)を分散させる設定があります。 AddToWorld時の 1フレームの処理負荷制限 投入されるオブジェクトの単位 RemoveFromWorldの 1フレームの処理負荷制限 投入されるオブジェクトの単位
  23. 23. AddToWorldの最適化 EventBeginPlayの処理を減らす • BeginPlayに高負荷な処理をしているとAddToWorldに影響 • ConstructionScriptに逃がせるものは逃がすなど
  24. 24. 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の負荷を減らす
  25. 25. アジェンダ 1. Load時間(及びパッケージサイズ)のProfilingとOptimization 2. GCのProfilingとOptimization
  26. 26. GARBAGE COLLECTIONの PROFILINGとOPTIMIZATION
  27. 27. Garbage CollectionのProfilingと最適化手法 • GCの問題点 • GCの考え方 • GCコストの計測方法 • タイトルでやることできること
  28. 28. GCの問題点 • GCが起きたときの急激なヒッチ – レベルストリーミングにより。。 • 遷移時のシーンのオブジェクトの一時的な増加 • 遷移後の大量のオブジェクトの破棄
  29. 29. GCの考え方 UObjectArray: UObject(Uproperty)の一次元配列 UE4はUobjectすべてを一次元配列で管理 つまり、 GCのコスト = 検索コスト + 削除コスト GCが起きると、このすべてのオブジェクトを検索し、不要なものを削除する
  30. 30. 削除コストの詳細 GCのフレーム分散 GCのコスト = 検索コスト + 削除コスト と記載しましたが、正確ではありません 削除コスト = 依存関係の切断 + 実際のオブジェクトの削除 GCが呼び出されたフレームのインパクト = 検索コスト + 依存関係の切断 こちらはフレーム分散可能 今日はこれを便宜上「削除コスト」と呼びます
  31. 31. つまり、 GCのコスト = 検索コスト + 削除コスト
  32. 32. エンジン最適化による高速化 • エンジン内部の最適化で4.16で高速化が実現 • 例: – 検索: 40ms -> 12ms – 削除: 70ms -> 48ms
  33. 33. (発展)レベルストリーミングアウトのときの 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);に。
  34. 34. GCコストの確認方法
  35. 35. GC関連コストの確認方法 1. Log loggarbage log (Log loggarbage verbose) 2. Stat dumphitches 3. (Stat Startfile/Stopfile) 4. CBD Profiling Tools 5. Obj –list / Bluerprint Stats
  36. 36. 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. 検索コスト 削除コスト
  37. 37. 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 -
  38. 38. 注意点: VERIFY GC ASSUMPTIONS GCの本来のコストを見積もるならば、 TESTビルドで検証することを強くおすすめします。 • Development Buildで基本ON (Test/Shippingでは基本無効) • CollectGarbageInternal.VerifyGCAssumptionsが走ってGCが激重に • なしにするには、実行時に”-NOVERIFYGC”オプションをつけて起動
  39. 39. 3/5: Stat startfile/stopfile
  40. 40. 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
  41. 41. 5/5 シーン内にどんなUobjectがあるかを調査するコマンド Obj list Class Count MetaData 357 SkeletalMesh 1 Package 575 Class 2393 FontFace 6 BoolProperty 4797 FloatProperty 3788 ObjectProperty 3251
  42. 42. 5/5 シーン内にどんなUobjectがあるかを調査するコマンド Total,DOBP, NumNodes, …,PureTotal,UserFnCount,UserMacroCount,NumSubobjects,NumPropertyObjects 1268,338,105592,18571,2893,2341,19076,15812,2099,305,669,193895,85528 Bluerprint Stats Plugin
  43. 43. タイトルでできること
  44. 44. タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。) <-最初に言及
  45. 45. GCのためのレベル分割 • レベルストリームで大きく読み込むと、 一時的にシーン全体のオブジェクトが増え、検索コストが増加する。 • レベルストリームで大きく捨てると、削除コストが増加する。 • GCが小さくなるように、サブレベルを更に細かく分割し、 細かく読み込み、細かく捨てる。 • 最後の最後の手だが、この方針で行くならば、 早めに検証しサイズを見積もらなければいけない。
  46. 46. タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。)
  47. 47. DisregardGCObject Concept • ゲーム起動時から常にメモリにいて欲しいオブジェクトは必ずある。 絶対にGCしてほしくないので、そもそもそれらをGC対象から外す。 • ゲーム起動時(PreInit())に読み込むものをGC対象外とする。 UObjectArray: UObject(Uproperty)の一次元配列 ゲーム常駐部分
  48. 48. DisregardGCObject Maximum Object Count Not Considered By GC Sizeo Of Permanent Object Pool この2つの数値を設定します。
  49. 49. DisregardGCObjectの設定方法 1. DisregardGCObjectの有効化 [/Script/Engine.GarbageCollectionSettings] gc.MaxObjectsNotConsideredByGC=1 gc.SizeOfPermanentObjectPool=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. 3. この数値を、先程の値に再設定 [/Script/Engine.GarbageCollectionSettings] gc.MaxObjectsNotConsideredByGC= 52083 gc.SizeOfPermanentObjectPool= 9937152 0以外の数にすることで有効になる 上が、PreInitでロードされたUObjectsの総数 下が、PreInitでロードされたUObjectsの総バイト数
  50. 50. DisregardGCObject: 成果 設定前 LogGarbage: 74.040701 ms for GC LogGarbage: 2.808510 ms for unhashing unreachable objects. 設定後 LogGarbage: 60.583722 ms for GC LogGarbage: 2.114550 ms for unhashing unreachable objects. 20%ほど改善
  51. 51. タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。)
  52. 52. 効率的なクラスタの模索 Concept: 複数のUobjectをクラスタ化して、検索コストを減らす。 ※本件は削除コストには効果がありません。 上記四項目、 ActorもClusteringに含めるか、BlueprintもClusteringに含めるか。。
  53. 53. 効率的なクラスタの模索 Actorの中にCanBeInCluster設定項目がありま す。 これで、クラスターの中にそのアクターを含め るかどうかを設定できます。 ※DefaultではStaticMeshActorはDefaultで True。
  54. 54. 効率的なクラスタの模索: 注意点 Clusteringされるタイミング クラスタが作成されるのは、ロードされ、 AddToWorldされるタイミングです。 その後、新たな参照が貼られる様なオブジェク トは、CanBeInClusterをOffにしなければいけ ません。 Matinee/SequencerでStaticMeshActorを動 かすときなどは注意。 (Developmentビルドならば、エラーを出して クラッシュするので調査は簡単です。)
  55. 55. Blueprint Clustering Blueprintは内部に沢山のUObjectを保持しています。 なので、ブループリント単位でのクラスタリングを行なうと、検索コストをへら すことが可能です。 と言いつつ、このオプションでクラッシュが発生するとの報告があります。 それらが、回避可能か、エンジンとして修正可能かの情報を集めています。 もしも、クラッシュした場合、UDNにてご報告頂ければ幸いです。
  56. 56. 効率的なクラスタの模索: 効果 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
  57. 57. タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。)
  58. 58. Uobject 削減の模索 UObjectArray: UObject(Uproperty)の一次元配列 UObjectが多ければ必ずヒッチが生じる Uobject自身を不必要に増やさない対策
  59. 59. Macro -> FunctionによるUPropertyの削減 Concept: マクロの中にマクロが含まれている -> 最終的に展開されたBPは非常に巨大なものに 例)あるBPから一つのマクロを取り除く -> プロパティー数が1066 -> 600まで減る マクロは、中にlatentノードがなければ関数化によって取り除くことができます。 上記手法によりあるBPを関数化した場合、 とあるマップで読み込んでいるキャラクター関連のUObjectが 20781から5486へと75%のプロパティ数の削減を実現しました
  60. 60. 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: MacroName,NumFlatInstances,NumProperties LogBlueprintStats: ForEachLoop,850,954 LogBlueprintStats: ForEachLoopWithBreak,225,396 LogBlueprintStats: PlayTalkEndAnimation,319,294 LogBlueprintStats: PlayerMoveTo,12,144 LogBlueprintStats: CS Character Setting,0,116 LogBlueprintStats: End Cutscene Matinee,9,99 マクロに特化した情報が出るように 拡張されたコードがあり。。 (4.17で正式採用予定) 欲しい人はご連絡ください。
  61. 61. タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。)
  62. 62. Blueprint NativizationによるUobjectの削減 BP Officeでテスト 非Native: Total 31359 Objects Native: Total 30885 Objects 差: 475 Objects ---主に減っている箇所 StructProperty 4901 4756 FloatProperty 3450 3356 ObjectProperty 2595 2569 IntProperty 1528 1482 BoolProperty 3653 3617 Native化対象のBP BP Office Sample
  63. 63. タイトルでできること – DisregardGCObject – 効率的なクラスタの模索 (Can Be In Cluster) – Uobject 削減の模索 • 例: MacroによるUObjectの肥大 • 例: Nativization によるUobjectの削減 – レベル分割 (GCのための。。。)
  64. 64. アジェンダ 1. Load時間(及びパッケージサイズ)のProfilingとOptimization 2. GCのProfilingとOptimization
  65. 65. まとめ • コンソール開発は、 常にパフォーマンスを意識して開発する必要があります • なるべく早期から、実機で、定期的な パフォーマンス検証を行ってください • UE4は調整、検証を行える様に様々なツールを用意しております • 疑問点やヘルプは、 UDNもしくは弊社サポートまでお問い合わせください

×