新しいMemory Profilerパッケージによるメモリの検査

このブログでは、ゲームのメモリ関連の問題を診断および調査するために使用できる、新しいMemory Profilerパッケージの5つの主要なワークフローについて説明します。これらは以下の通りである:
- アプリケーションのメモリ圧力を監視する
- Unityオブジェクトの分布を見る
- 設定不良資産の検出
- 意図しない重複オブジェクトを見つける
- 最適化を検証するためのメモリ・キャプチャの比較
メモリ・プロファイラの紹介については、最近のブログをご覧ください、 Memory Profiler 1.0.0について知っておくべきすべてのこと.
この最初のワークフローは、アプリケーションがデバイスのメモリリソースをどの程度要求しているかを監視する。このプロセスは、あなたのアプリケーションがパフォーマンスの問題を起こす危険性があるかどうか、あるいはメモリ消費量が多すぎるためにオペレーティングシステムに追い出され、終了させられる危険性があるかどうかを判断するのに非常に重要です。
まずはじめに、ターゲット・デバイス上で動作するサンプル・ゲームのビルドを用意する。当然ながら、実際のハードウェア上で動作するゲームのメモリーキャプチャーを行い、デバイスが利用可能なメモリーリソースをどのように使用しているかを確認することが不可欠である。さらに、Unityエディタでは、メモリはUnityランタイムと同じようには動作しないため、プレイモードでエディタのメモリキャプチャを取ることは、ゲームのメモリがデバイス上でどのように見えるかを表現するのに適していません。(エディターのメモリーキャプチャーを取ることは、カスタムエディターウィンドウなど、エディター用のツールを開発するときに適切です)。
メモリ使用量を分析したいゲームのステージに移動したら、Memory Profilerのドロップダウンを使ってデバイスにMemory Profilerを取り付けます。そして、以下のようにメモリーキャプチャを取ることができる。

このキャプチャを開くと、Memory ProfilerはSummaryページの一番上に、アプリケーションのメモリフットプリントを "Memory Usage On Device"として表示します。

ここでは、利用可能な3.50GBのうち、アプリケーションのメモリフットプリントが492.5MBであることがわかる。次に、キャプチャ時に使用するデバイスの物理メモリ(RAM)の割合が妥当かどうか、最善の判断を下す必要がある。デバイスの物理メモリは、実行中のすべてのプロセスで共有されることを忘れないでください。
このビジュアル・インジケータが常駐 メモリの 合計を示していることに気づくだろう。総常駐メモリとは、アプリケーションのメモリのうち、どれだけがデバイスの物理メモリ・ハードウェア(RAM)に常駐しているかを意味します。これは、2つの理由から、あなたのアプリケーションの現在のメモリ使用量がターゲット・デバイス上でどの程度厳しいかを示す最も明確な指標となる。まず、アプリケーションの総常駐メモリ使用量が増えると、オペレーティング・システムがデバイスの物理メモリに仮想メモリをページ分割して出し入れするページ・フォールトが頻繁に発生する可能性が高くなるからです。ページフォルトが頻発すると、アプリケーションのパフォーマンスが著しく低下する。そして第二に、多くのオペレーティング・システムは、アプリケーションの常駐メモリ使用量を使用して、そのアプリケーションの現在のメモリ・フットプリントを決定するからである。アプリケーションのメモリフットプリントが大きくなりすぎると、オペレーティングシステムはアプリケーションを退去させ、終了させます。
そのため、Memory ProfilerのMemory Usage On Deviceビジュアルインジケータを使用することで、キャプチャ時にメモリが過剰に使用されていることが原因で、アプリケーションにパフォーマンスの問題が発生したり、オペレーティングシステムによって終了されたりする危険性があるかどうかを推測することができます。
これは、Allocated Memoryとは対照的で、Committed Memoryと呼ばれることもあり、このインジケータの下に様々なグラフィックで表示されていることに気づくかもしれません。割り当て済みメモリーとは、物理メモリーに常駐しているかどうかに関係なく、アプリケーションが現在割り当てているすべてのメモリーのことです。常駐メモリ使用量は、アプリケーションがハードウェアに負担をかけているメモリ使用量を把握するための鍵となります。
Memory ProfilerのUnity Objectsタブでは、Unity Objects(アプリケーションのテクスチャ、シェーダ、メッシュ、マテリアルなど)の観点からアプリケーションのメモリの概要を見ることができます。というのも、Unity Objectsは多くのUnityユーザーにとって本質的に馴染みのあるものであり、Unity Editorで直接扱うものだからです。これは、アプリケーションのメモリを理解するための馴染み深い入口を提供するだけでなく、Unity固有のコンテキストを提供することで、潜在的なさまざまな問題の診断と修正にも役立ちます。

Unity Objectsビューを見るには、上図のようにメモリキャプチャを開いた後、Memory Profilerの上部にあるUnity Objectsタブを選択するだけです。
Unity Objectsビューを見ると、アプリケーション内のUnity Objectタイプの分布がすぐにわかるのがわかります。これにより、キャプチャー時にどのタイプが最もメモリを消費していたかをハイレベルで理解することができ、また、例えば、特定のシーンがAudioClipオブジェクトを多用することが予想されるかどうかなど、推論することもできる。各タイプを展開すると、以下のように、現在割り当てられているUnity Objectを個別に表示することもできます。

Unityオブジェクトは、アプリケーションの総割り当てメモリの一部を占めていることを覚えておくことが重要です。その金額は、表の上にあるインジケーターで確認できる。

ここで、"Total Memory In Snapshot "という総割り当てメモリサイズが4.64GBで、Unityオブジェクトがそのうちの2.37GBを占めていることがわかります。さらに、例えば検索機能を使ってテーブルをフィルタリングすると、このバーが検索結果を反映して更新されることに気づくだろう。つまり、現在表に表示されているすべてのメモリのサイズが表示される。これによって、キャプチャ全体に占めるメモリの割合を正確に把握することができ、最適化のための努力をどこに投資すべきかを知ることができる。

Memory Profiler のバージョン 1.0 では、Unity Objects テーブルは Allocated Memory を表示します。これにより、どのUnityオブジェクトが現在物理メモリに常駐しているかを正確に確認できるようになり、アプリケーションの現在のメモリフットプリントに直接寄与しているオブジェクトを正確に確認できるようになります。
All Of Memoryタブを使用すると、キャプチャ時にアプリケーションの残りのメモリを検査できます。このメモリには、さまざまなUnityサブシステム、マネージドオンリー(C#)メモリ、DLLや実行可能ファイルなど、Unityオブジェクト以外のメモリが含まれます。
Unity Objectsビューは、様々な潜在的な問題を診断するのに役立ちます。そのような問題の1つは、必要以上にメモリを消費する原因となる、不適切に設定されたアセットを検出することである。
下のキャプチャでは、Unityオブジェクトのかなりの部分がテクスチャであることがわかります。このキャプチャは、高解像度レンダーパイプラインを使用し、視覚効果を多用したグラフィックの忠実度が高いプロジェクトのものです。このような背景を念頭に置いて、我々はテクスチャーの多用を期待している。

しかし、2番目の大きなカテゴリーである「Texture2D」を拡大すると、2つのテクスチャが他のテクスチャよりもはるかに大きく見えることに気づく。私たちのプロジェクトの理解では、これらのテクスチャは、HoloTable_Normalや HoloTable_Maskのような同等のテクスチャよりも大きいことに驚きます。

そこで、このテクスチャーの詳細を知るために、表の中からひとつを選び、その原因が何なのかを調べ始める。テクスチャは書き込み可能、つまり "Read/Write Enabled "である。

これは、多くのユーザープロジェクトで見られる一般的な問題です。テクスチャのインポート設定で「Read/Write」設定をチェックすることで、不要なテクスチャを誤って書き込み可能にしてしまうのです。テクスチャがこのフラグを有効にすると、メモリ上のサイズが2倍になります。これは、CPUでアクセスできるようにテクスチャーデータの2つ目のコピーが必要なためです。この兆候は、テクスチャのTotal Sizeが予想したサイズの2倍、または類似のテクスチャの2倍であることです。
これら2つのテクスチャの「Read/Write」フラグを無効にして2回目のキャプチャを撮ると、これら2つのテクスチャのサイズが半分になったのがわかる。

この例のように、Unity Objectがグラフィックメモリを割り当てているケースを簡単に見つけられるように、将来のリリースでUnity Objectsテーブルにグラフィック(GPU)メモリの列を追加することを検討しています。
Unityプロジェクトでよく見かけるミスは、意図せずにUnity Objectを重複して作成してしまうことです。例えば、MeshRendererのマテリアルプロパティにアクセスすることで、誤って重複したマテリアルを作成することは非常に簡単です。例えば、特定のMeshRendererのすべてのインスタンスでこれを行う場合、このようなことはすぐに増えるだけでなく、さらに、これらの動的に作成されたマテリアルは明示的に破棄しなければなりません。
この種の問題を見つけるために、Unity Objects テーブルには、重複する可能性のある Unity Objects のみを表示するクイックフィルタが用意されています。このビューは、同じ名前とサイズを持つ複数のインスタンスを持つUnity Objectsのみを表示するようにテーブルをフィルタリングします。重要なのは、重複の可能性の多くは予想されることであり、まったく心配する必要はないということである。例えば、プレハブの複数のインスタンスは、同じ名前とサイズのTransformコンポーネントを持つかもしれません。このワークフローでは、以下の例で説明する。
下のキャプチャは、Doorプレハブのインスタンスが2つあるシンプルなシーンで撮影したもので、Unity Objectsテーブルの下にあるShow Potential Duplicates Onlyフィルターを有効にしています。これは、同じ名前とサイズを持つ複数のインスタンスを持つUnity Objectsのみを表示するようにテーブルをフィルタリングしています。

シーンに2つのDoorプレハブのインスタンスがあるので、予想通り、すべての関連オブジェクトのインスタンスも2つあります:MeshRenderer、Transform、GameObjectなど。しかし、上のキャプチャには「ドア」素材も2つある。これらのドア・インスタンスは、私たちのシーンでは同じように見えるので、マテリアルを共有することが予想されます。したがって、これは意図しない重複であり、この特定の例では、プレハブ内のMeshRendererのマテリアル・プロパティにアクセスしたことが原因です。このプロパティアクセスを削除して2回目のキャプチャを取ると、Unity Objectsテーブルに重複したマテリアルがもはや存在しないことがわかります。

このフィルタは、単に同じ名前とサイズのインスタンスを複数持つUnity Objectsをすべて表示するものであることを覚えておくことが重要です。あなたが目にした重複の可能性が、予期されたものなのか、それとも実際には意図的でないもので、調査すべきものなのかを解釈するには、あなたのプロジェクトに関する知識が必要です。上部にある「Total Memory In Table」バーに注目することをお勧めします。これは、アプリケーションに割り当てられたメモリのうち、表に表示されているメモリの割合を視覚的に示すものです。これは、最適化の努力をどこに投資すべきかという視点を維持するのに役立つ。
メモリ・プロファイラには、2つのメモリ・キャプチャを比較する機能もある。これによって、たとえば発見した問題に対処するためにプロジェクトに変更を加え、その変更が本当に望ましい結果をもたらしたかどうかをテストすることができる。仮説が正しく、変更が実際のハードウェアに望ましい結果をもたらしたかどうかを常にテストすることが重要である。ここでは、この比較ワークフローの例を探ってみよう。
以下は、最初のレベルで撮影されたモバイルゲームのキャプチャである。Unity Objectsの最大のカテゴリはTexture2Dであることがわかります。このカテゴリを開いて最大のテクスチャをチェックすると、UIテクスチャがいくつかあり、それぞれメガバイトと、他のゲームに比べてかなり大きいことがわかります。これは私たちに疑念を抱かせる:なぜこのテクスチャーは他のものよりもこんなに大きいのですか?Memory Profilerでテクスチャを選択し、"Select In Editor "ボタンを使用すると、Projectウィンドウでソーステクスチャアセットがハイライトされます。

インスペクタウィンドウを使用すると、「NPOT」(non-power-of-two)テキストが示すように、問題のある大きなUIテクスチャはすべて、寸法が2のべき乗でないために圧縮されていないことがわかります。

これはテクスチャーのサイズが大きいことの説明にもなる。このメモリ使用量を減らすために、私たちのプロジェクトに関する知識を使うことができる。これらのテクスチャのうち3つ(ヘルプコントロール)は、他の3つのテクスチャ(クリーチャー)と同様に、UIで常に一緒に表示されることがわかっている。したがって、3つのテクスチャのセットごとに2つのスプライトアトラスを作成すれば、メモリ内のテクスチャの数を増やすことなく圧縮できるため、割り当てメモリの使用量を減らせるという仮説を高い信頼性で立てることができます。

2つのスナップショットを比較するには、まず最初のスナップショットを開きます。これが、私たちが比較したい "ベース "である。開いているスナップショットの上で、"Compare Snapshots "タブを選択し、2番目のスナップショットを選択します。これでMemory Profilerは、2つのスナップショットを比較したサマリーを以下のように表示します。

変更の効果を確認し、Texture2D カテゴリに割り当てられたアプリケーションのメモリサイズが実際に小さくなったことを確認するには、Unity Objects タブを選択します。ここでは、変更されたUnity Objectのタイプと、キャプチャ間でどのように変更されたかを示す比較表が表示されます(下図)。

Texture2Dタイプ全体のサイズが3.6MB小さくなり、テクスチャが4つ減ったことがわかる。このカテゴリを拡大すると、個々の非圧縮スプライトテクスチャを削除し、2つのスプライトアトラステクスチャを追加した結果、正味3.6MBと4つのTexture2Dオブジェクトが削減されたことがわかります。

つまり、これは成功だったのだ。比較機能を使って我々の仮説が正しいことを確認し、割り当てられたメモリ内のこれらのテクスチャのサイズを小さくすることができた。
このブログを読めば、新しいMemory Profilerパッケージの5つの主要なワークフローについて、よりよく理解できるはずだ。これらのワークフローは、ゲームのメモリ関連の問題を診断および検証するために設計されています。Unity 2022.2でリリースされたMemory Profilerパッケージが、ゲームのメモリフットプリントをよりよく監視、調査、理解するのに役立つことを願っています。パフォーマンス・プロファイリングツールの改善に関するご意見、ご感想は、フォーラムページ、またはロードマップページからお寄せください。
このトピックの詳細にご興味があれば、アプリケーションのメモリー・フットプリントがどのように計算されるのか、常駐メモリーや割り当てメモリーなどのトピックをより深く掘り下げた別のブログを数週間以内に公開する予定です。
