アセットバンドル(Asset Bundle)粒度の計画

今回の主な話題:「アセットバンドル(Asset Bundle)粒度の計画」、「Androidでのマルチスレッドレンダリング」、「Instantiateインスタンス化中のMonoメモリの割り当て」、「アニメーションモジュールで関数のCPUの割り当て」。


アセットバンドル(Asset Bundle)

 

Q1:アセットバンドル(Asset Bundle、以下「AB」)の粒度の分割問題に対して、私が担任するプロジェクトには、論理や最終のパッケージのサイズで決定します。例えば、圧縮後のABパッケージをできれば1MBを超えないようにさせます。これはUWAが推薦した値です。また、現在AndroidパッケージのSerialized Fileの占有率も考えの一つ。そのため、この部分のコードは基本的に変更できません。(人工的に共通パッケージを複数に分割することや、ABパッケージの内容を人為的に分割することが含む。)これで目標機能が実現できるけど、自動化の程度は足りません。毎回新しいアセットを加える時、この手動的な流れをもう一度行わなければなりません。別人からのオープンソースも研究し、全自動的に依頼関係を分析したけれども、その問題は最後の粒度は高すぎで、科学的ではありません(でもこの勉強方法はおすすめ)。UWAのみなさんはどうやってこの問題を解決しますか。

これは相当オープンな問題です、人々にはそれぞれの見解があります。いまのUWAには直接の答えはできませんけれど、二つのポイントに注目できます。

⑴ベストのパッケージ方法はありません、ただ、プロジェクトに対して最適な方法があります。

⑵高い粒度も低い粒度も、採用している成功の例があります。つまり、要求に合うなら、どちらも良い方法です。

これから、粒度問題について、UWAの見解を述べます。

⒈高すぎる粒度は推薦しません、特にアセットとABの一対一。

高すぎる粒度は、IOロード回数の多すぎを引き起こします。それでハードウェアデバイスのバッテリー消費と発熱を加速させます;一方、私たちがテストしました、Unityバージョン5.3〜5.5のAndroid環境でアンロードしない場合には、毎回ABがアップロードする時、各個ファイルのSeralizedFileのメモリ占有は一緒512KBです(ほかの環境よりすごく高い)。そのために、メモリに大量なABが含まれある時、SerializedFileのメモリ占有は非常に高くなります。ちなみに、この問題はもうUnityバージョン5.6に改善しました。

⒉Unityバージョン5.3以後、ABファイルのサイズは1MB以下を限りの必要はもうありません。

前にUWAはこの限りを設定しましたのは、二つの考えがあります。一つは5.3以前のNew WWWは最も頻繁なABロード方式を採用しました(今も同じけど)。その頃、New WWWのロードは少し大きなWebStreamを形成しました。普段は圧縮したABの4〜5倍で、多くのメモリを占めました;もう一つ、ABは大きい時(例えば5MB以上)、ロードコストも大きくなります。子スレッドで実行しますのため、Profilerに反映する時、皆さんは一つの「おかしい」CPU高コストを見えます--Graphice.PresentAndSync。そのため、UWAはたくさんの実験をやりましたし、「ABは1MB以下に圧縮したら、ロードに納得できます」という結論が取得しました。ただ、Unity5.3以後、LZ4の導入に従って、たくさんの状況もう変わりました。Chunkロードの特点により、ABのロードは早くなり、メモリ占有も前より低くなりました。そのため、LZ4のABには少し低い粒度も考えられます。

 

しかし、まだ三つ注意すべき点があります。

 

⒈ ホットアップデートが需要なABに対して、QAに他の人の言うとうりに、実際によりABサイズのコントロールは必要です。

⒉ 同じLZ4のABですけど、ロード方式が違うなら、ロード効率の全然不一致も可能です。以下は私たちがUWA Day 2017にシェアした内容です。二つのAB(LZ4様式)で同じアセットをロードします。違うのは一つのABにアセット10個含みます、もう一つABにアセット30個含みます。下記の図は三つ方式でABのロード始まりからAB.Loadまでの時間コスト比較です。New WWWロードに明らかな時間差は簡単に見えます。ですから、Unity 5.3以後、できればLoadFromFile(Async)でABをロードします。

⒊ ABのパッケージするに対して、できれば論理的に同時出現(一つのPrefabにShare以外のAsset)、些細なアセット(Shader、Material、粒子システムなど)を一つにパッケージし、LoadAllでロードを行います。何故ならそれでもっと高い効率ができます。下記の図はLoadAllとLoad One By Oneのパーフォマンス比較です。私たちがやった実験に、確かにLoadAllのパーフォマンスは比較的に良いです。

上記はUWAから開発チームへのヒントです。皆様のプロジェクト最適化にお役に立ちをご希望おります。


パーフォマンス最適化

Q2:前のUWA講座で、「モバイル端末でマルチスレッドを開き,後期効果をレンダリングスレッドに移動すると、後期のコストを節約することができる。」と聞きました。自分がやってみた時、確かにメインスレッドに後期に表示されたCPU占有は低下していましたが、Gfx.WaitForPresentの時間が増えてしまいました。最終的に両者の加算はほぼ同じになりました。ではマルチスレッド機能を開く後、後期に役に立ちますか。

私たちはUWA Dayで詳しくマルチスレッドレンダリングのコストを検討しました。注意すべきのは、一般的にマルチスレッドレンダリングを開く後、一般的にメインスレッドのレンダリング時間コストが大幅に短縮できますが、それ自体の総計算量を削減することはありません。基底アルゴリズムやハードウェア上の高まりではなく, 計算の一部分をメインスレッドから子スレッドに移すことです。したがって、マルチスレッドを開く利点は、他の時間のかかるモジュール(コード論理など)を実行しますために、メインスレッドに大量の空間をもたらすことであす。しかし、これは「マルチスレッドレンダリング機能を開いたら全て解決」の意味ではありません。もしレンダリングモジュール自身のコストが高いなら、子スレッドも同じ時間かかり、更にメインスレッドが子スレッドを待つ現象、すなわちWaitForPresentのコストが高い状況になる可能性もあります。そのため、マルチスレッドを開くことはメインスレッドのレンダリング圧力を下げられますが、必ずフレームを大幅に引き上げるではありません。各開発チームは自分のプロジェクトにテストや比較してみてください。


Instantiateインスタンス化

Q3:なぜUnityに一つのPrefabをインスタンス化するとそんなに多くのGC Allocatedが発生しますか。

図中のInstantiateインスタンス化操作は、インスタンス化に一つの少し複雑なUIインタフェースです。Active/Deactiveによって一部分のUIインタフェースのオンとオフも含まれます。Instantiateインスタンス化は、オブジェクトにスクリプトを積み込む構造関数(..ctor操作)やAwake操作。これらの関数のMonoメモリの割り当ては全部Instantiateインスタンス化操作の中に計算されています;また,UIインタフェースのActiveとDeactiveは,UIコードの下位層に関するOnEnableやOnDisable操作を引き起こし,同様に一定のMonoメモリの割り当てをもたらします。上記の図でMonoメモリの割り当てが高いのは,InstantiateやActiveの呼び出し回数が多いのためです(Instantiate 93回;Active 183回)。そのため、図中のMonoメモリの割り当てが高すぎるのは、一つのPrefabのインスタンスではありません、一つのフレームに大量のUI Prefabをインスタンス化やActiveしたからです。開発チームに「プロジェクト実行中のUIインスタンス化頻度を検討する」をアドバイスします。


アニメーションモジュール

Q4:アニメーションモジュールにAnimators . ProcessAnimationsJobとAnimators . WriteJobのCPU占有率が高い。これらはどのような要因がありますか。

Animators.WriteJobはモデルの骨格の数からの影響は大きいです(animation curvesからの影響は少ない)。骨格の数が多いほど、時間がかかります。また、Optimize GameObjectオプションをオンにしますと、関数の時間コストは下げられます。Animators.ProcessAnimationsJobも同じ骨格の数に大きく影響されています。両者の数が多いなら、この関数の時間コストは高いです。


UWA公式サイト:https://jp.uwa4d.com

UWA公式ブログ:https://blog.jp.uwa4d.com

UWA公式Q&Aコミュニティ:https://answer.uwa4d.com