ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
ゲームのメモリをプロファイリングして最適化する
Appleプラットフォームが、ゲームに必要なメモリの計算や割り当てを行う仕組みをご覧ください。InstrumentsやGame Memoryテンプレートでゲームのプロファイルを作成したり、メモリグラフで現在のメモリ使用状況をモニタリングしたり、Xcode Memory Debuggerやコマンドラインツールで分析したりする方法を紹介します。また、Metal DebuggerのMetalリソースをはじめ、メモリ使用率を最適化するためのヒントやコツについても解説します。
リソース
関連ビデオ
WWDC23
WWDC22
WWDC21
-
ダウンロード
♪ ♪
「ゲームのメモリをプロファイリングして 最適化する」へようこそ AppleのGPU Software Teamの Jack Xu (许) です Seth Lù (陆) も参加してくれます ここ数年 私達チームは皆さんのような ゲーム開発者とともにゲームメモリを理解し 改善するため一緒に取り組んできました 今日は私たちが得た 学びを共有したいと思います 皆さんもゲームメモリをデバッグ 最高のプレイヤー体験を 提供する素晴らしいゲームを作る際 先手を打てるはずです
CPU GPUの両オブジェクトから ゲーム内のメモリ使用量を分解 さらにゲーム内の割り当て物理メモリの 実際の使用状況オブジェクト間の 参照などを分析します メモリには様々な側面があるため 開発ツールも様々な角度から メモリの謎を解き明かします ガイドツアーでは Xcode インストゥルメント ターミナルでの コマンドラインツールなど 使い方を体験いただきます 本日はまずゲームメモリを理解 するための前段階を説明します プロファイルメモリとメモリ成長を説明し Sethがインストゥルメントに ついてお話しします インストゥルメントについて 話した後Xcodeとターミナルの ツールを使ってゲームの メモリグラフを分析します これらのワークフローはメモリ使用の現状と ゲーム全体のメモリの内訳に着目しています 最後にSethがMetalデバッガーを 使ってMetalリソースを 最適化する方法を紹介しますMetalリソースは ゲームメモリ中でもやや独立した しかし中核となる領域です さてゲームメモリを理解する ところから始めましょう Modern Rendering with Metalの サンプルコードなど Xcodeからゲームを起動すると デバッグナビゲータで このメモリーレポートを開くことができます ゲームの現在および直近のメモリ使用量 システムへの影響度を最初に確認できます
ゲージに表示される数字は 現在のゲームメモリ使用量を示します メモリデバッグの重要な第一歩は この数値の意味を理解することである
To put it in one line, ひとことで言うとゲームで実際に使う メモリはアロケーションとは異なります 実際の使用メモリは物理メモリ上です 一方アロケーションはゲームが要求するメモリで 仮想メモリアドレス空間上にあります そして異なるアロケーションは 当然別々に計算されます
ゲームメモリを割当てた時 それらの新しい割当てはすぐ また直接物理メモリ上のスペースを 占有するわけではありません 逆にシステムが各プロセスに提供する 仮想メモリアドレス空間上に ある程度の領域を確保することになる そして後で実際にプログラムが この割当てを使う時に システムは物理メモリ上にスペースを用意する
同じ種類の割当てはカテゴリーにまとめられ 仮想アドレス空間をまばらに占拠する これらのカテゴリーには: プログラムの実行バイナリです; 全ライブラリとフレームワーク; スタックは ローカル変数と 一時変数 および一部の 関数引数を格納するためのものです: 動的メモリ領域はヒープとも呼ばれます; クラスインスタンスのストレージや プログラムが手動で割り当てるメモリも含む; ゲームアセットファイルなど読取り専用 リソースからマッピングされた領域; そしてもちろん バッファ テクスチャ パイプラインの 状態オブジェクトなど ゲーム内の金属オブジェクト これらのカテゴリーは リージョンで構成されます メモリ操作はメモリページの単位で行われ 最新Apple製デバイスでは 1ページが16キロバイトになる つまり各領域は1ページ以上を占め 少なくとも16キロバイトの大きさである
ゲームが進むにつれてメモリは どんどん更新されます 新しいオブジェクトが割り当てられ 古いオブジェクトが破壊され 領域がどんどん変化していきます しかし領域上で使用されるページだけが 物理メモリ上にありシステムは他のAppと 同様にゲームに電荷を供給します
ゲーム内のメモリページは: ダーティ コンプレッション クリーンの3種類に分類される その内容を見てみましょう ダーティメモリページはゲームが 書込んだメモリが含まれます これにはヒープやフレームワーク でのメモリ割り当て ゲームでの変数やシンボルの変更も含まれます Apple siliconを搭載したデバイスでは アクセスされたMetalリソースも このカテゴリに入りますこれは CPUとGPUが同じ高速ユニファイド- メモリのプールを共有する為です
しかし一部のダーティページが 長期間使用されない場合 システムはこれらのページを 圧縮したりフラッシュや ディスクに格納したりして 物理メモリ上での存在感を 低下させることがあります これをスワッピングと呼びます これで端末はより多くのAppや サービスを実行できます その後ゲームがこれらの ページを再び要求したとき システムはディスクから それらいずれかを解凍します ただし非圧縮サイズでもチャージされます クリーンメモリページにはテクスチャや オーディオアセットなど ディスクからマッピングされた 読み取り専用のファイルや プロセスにロードされた フレームワークが含まれます システムはいつでもディスクを 空にしたり再ロードしたりでき ゲームのメモリフットプリントに カウントされることはありません しかしメモリ上に常駐することもあり 使いすぎるとシステムや ゲーム動作が遅くなります 通常最初の2つを見るのが最も興味深いですが これを合わせてメモリ- フットプリントと呼びます そしてシステムはこれを利用し メモリ制限を実施する
用語の中にメモリのフットプリントを意味する 「ダーティメモリ」という言い方がありますが これはダーティがクリーンの 反対語であることに由来します あいまいになってきたら どちらを指しているのか 呼びかけますのでご安心を これでメモリとゲームへのシステムチャージの 仕組みは理解頂けたと思います このXcodeのメモリゲージ 以外にもMacのアクティビティ モニタAppなどシステム上の 様々な場所でメモリ- フットプリントを確認することができます Appleの一部のプラットフォームで Appのメモリ制限の為使用されます この指標を利用してゲームの メモリ使用量も調整できます 現在のフットプリントと使用可能メモリを 照会する便利なAPIがあります ここでざっと見てみましょう iOS iPadOS tvOSのゲームで使用可能な システムメモリを取得するには ヘッダーファイルos/proc.hにある os_proc_available_memoryを呼び出します
またどのAppleプラットフォームでも メモリフットプリントについては “get pid “からプロセスIDを “rusage_info_current” (現時点ではバージョン6) データストアからproc_pid_rusageを介して 取得することが可能です そして物理的フットプリント又は最大物理的 フットプリントのプロパティを取得
このセクションではメモリに関する いくつかの概念を復習しました ゲームでの割り当ては 仮想メモリのアドレス空間で行われ ゲームからアクセスされると 16キロバイトのページとして 物理メモリ空間を占有することになります メモリフットプリントはゲームの 実際のメモリ使用量判断のため Appleプラットフォームにおける 主要かつ普遍的な指標です これにはダーティページ 圧縮ページ スワップページがありました Apple silicon搭載のCPUとGPUの オブジェクトが含まれています そしてメモリ制限の実施に使用されます ゲームはシステムAPIを呼出しフット- プリントと使用可能なメモリを取得できます さてメモリが舞台裏でどう 機能しているか分かった所で ゲーム内でどのように 見えるかを確認しましょう 続きはSethにお願いします ありがとう Jack さっそくゲームのメモリ増設を キャプチャしてみましょう 引き続きModern Rendererの サンプルを使用します Xcodeからゲームを実行するとメモリーゲージが 時間経過とともにメモリ- フットプリントを表示します しかしインストゥルメントでゲームを プロファイリングすることで より詳細なメモリ使用状況を把握できます ゲーム起動時に大量のメモリが 割当てられることがある為 既存の実行にアタッチするの ではなく新しいゲームの 起動からプロファイリングから 開始するとよいでしょう Xcodeからゲームのプロファイリングを 素早く始めるには 実行ボタンを長押し 「プロフィール」を選択します これで自動的に インストゥルメントが表示されます インストゥルメントAppには システムのさまざまな側面を記録する プロファイリングツールが集められており 記録されたデータを タイムライン上で視覚化できます 今年は新たにメタルゲームに おけるメモリの成長をより 把握できる「ゲームメモリ」 テンプレートが追加されました
このテンプレートはメモリ割当てを 履歴とともに記録する アロケーションおよびメタルリソースイベント メモリフットプリントを記録する VM トラッカー 仮想メモリアクティビティを 記録する仮想メモリトレース メタル関連イベントを記録するメタルAppおよび GPUの各ツール が付属しています
このデモでは3つのインストゥルメントに ハイライトを当てる: アロケーション メタルリソースイベント そしてVMトラッカーです その前にゲーム用のトレースを記録しておこう ここで録画ボタンを押すと録画が開始され 終了時は同じボタンを押し 録画停止するかゲーム自体を終了します インストゥルメントが モダンレンダラーを記録している間 別の方法でトレースを 記録する方法を紹介します xctraceコマンドを使用すると プログラムで記録を実行できるため 自動化ワークフローに有用な場合があります
さらにデバイス名を指定することで iPhone iPad AppleTVをターゲットとして 選択できます
さてインストゥルメントの トレースをキャプチャしたので まずアロケーションについて見てみましょう アロケーションツールでは メモリ割り当て そのサイズ オブジェクト参照カウントを詳細に確認できます ただしプライベート- メタルリソースは含まれない 統計ビューには全ヒープアロケーションと 匿名VMが表示されます
All Heap Allocationsにはオブジェクトを 含むmalloc’ed bufferが含まれ All Anonymous VMにはダーティな 可能性のあるVM領域が含まれます このカテゴリーにはメタルリソースも 含まれる事が分かっています
ではAll Heap Allocationsの 内部を見てみましょう 通常アロケーションが大きい程 最適化に適しています 単一の最大割当てを見つけるには サイズ表の列をクリックして サイズ別に並べ替えることができます
アロケーションの場合 この矢印をクリックするとSwiftと Objective-Cの参照カウントの 変化を確認できます
この大きなアロケーションを リストで選択した状態で インスペクタにアロケーションの履歴の スタックトレースが表示されます ボタンをクリックするとシステムライブラリや フレームワークを非表示にできます ここではスタックトレースによると モダンレンダラーがアセットを ロードする際にアロケーションが発生します
フレームをダブルクリックすると ソースコードも表示されます 少し戻って「All Anonymous VM」 カテゴリの中を見てみましょう
MetalゲームではIOAcceleratorや IOSurfaceのカテゴリに 多くのアロケーションを 見つけられるかもしれません IOAcceleratorのアロケーションは Metal リソースに対応します
スタックトレースからアセットをロードする時 これら問題が発生したことがわかります
IOSurfaceのアロケーションは ドローアブルに対応 スタックトレースにMetalKitのビューの ドローアブル要求が示されています
アロケーションインストルメントは デフォルトでサイズを視覚化します ただし別のルックスも用意されています アロケーショントラックの矢印ボタンで アロケーション密度を可視化する 表示モードをカスタマイズすることができます これによりグラフが更新され 時間経過とともに実行された 割当ての量が表示されメモリ 割当てのスパイクが明らかになる このスパイクがメモリ増加の 原因の可能性があります アロケーションに表示される データは低レベルのものです 割当てられたMetalリソースを より深く理解するため Metalリソースイベントの説明に移りましょう MetalリソースイベントはMetalリソースを 中心に設計されたインストゥルメントです リソースイベントビューでは Metalリソースの割当てと 割当て解除の履歴を見ることができます ここではMetal APIを通じてプログラム的に 指定できるラベルによりMetal リソースを識別できます またアロケーション-インストルメントと同様に インスペクタで履歴の スタックトレースを確認できます
このインストゥルメントではMetal- デバイスの下にアロケーションと ディアロケーションの トラックを追加しています イベント密度を可視化するのに役立ちます ここまでアロケーションと Metalリソースイベントで メモリ割当ての理解が深まったと思います しかし常にメモリフットプリントに 反映されるわけではありません そこで次はVMトラッカーで 実際のメモリ使用量を調査しましょう
VMトラッカーインストゥルメントには 非圧縮のダーティメモリと 圧縮またはスワップされたメモリが表示される 「ダーティサイズ」は非圧縮の ダーティメモリを表します 「スワップサイズ」は圧縮又は スワップされたメモリを表します この収録ではモダンレンダラーに よる圧縮メモリーや スワップメモリーの使用はありません 「サマリービュー詳細(Detailed Summary view)」には VMリージョンが表示されます また「マップドファイル」領域には ゲームアセットなど メモリマップされたリソースが 含まれることがあります モダンレンダラーがビストロ- アセットファイルをメモリにマッピングします 以上インストゥルメントの アロケーション メタルリソースイベント VMトラッカーについて簡単に説明しました メモリ増加のプロファイリング 方法を簡単におさらいします: まずゲームメモリテンプレートを選択します そして そのトレースを記録し解析する メモリの増加パターンを再現・検証する際に この作業を数回繰り返すこともあります 新しいゲームメモリテンプレートが メモリ割当てやフットプリントの増加を より理解するために役立つことを願っています インストゥルメントの使用方法について 他のビデオもご覧ください ここでJackに戻します
ゲームメモリー- テンプレートはクールな機能ですね 時間の経過に伴うメモリー使用の変化を 理解するのに役立ちそうです さらにある時点でのゲームメモリ状態を 把握することでそのメモリ状態を深く掘り下げ さまざまな角度から検証できます そのためにメモリグラフなど 一連のツールを用意してます
メモリグラフはオブジェクトの作成履歴や参照 圧縮やスワップなどゲームのメモリ状態の 完全なスナップショットを 効率的に保存するためのファイルです
問題が発生した時あるいは 比較のため 問題発生の 前後など いつでも好きな時に スナップショットを撮ることができる スパイスとしてメモリグラフを 使ったメモリ解析の方法を 料理本に例えて説明しましょう 原材料と調理法をお見せします
For the Ingredients, well, you would need your game; something called Malloc Stack Logging; 具体的には:ゲームMalloc Stackという状況ログ そしてメモリグラフを キャプチャすることが必要です
Malloc Stackログはゲームプロセスに おける割当て情報を記録する これはスキーム設定にあります ランアクションを選択し診断に進み Malloc Stackログチェックボックスに チェックを入れます
全てのアロケーションと空き履歴は 割り当てが解除された後も 全てのオブジェクトを記録します ログデータは多くのメモリを 消費するかもしれませんが 断片化などの問題をデバッグするのに便利です 一方「ライブアロケーションのみ」は 割当て解除されたオブジェクトを 履歴から破棄するためより軽くなる 今回はライブオブジェクトの 参照だけを調査しているので このオプションを選択することができます 実際ほとんどの場合 「ライブアロケーションのみ」が 推奨されるでしょう
またXcodeから起動しない場合は 環境変数を設定することも可能です mallocマニュアルページで いくつかの追加記録モードを チェックしてみてください その後メモリグラフも用意 デバッグエリア内の 「デバッグメモリグラフ」ボタンを クリックするだけです Xcodeはメモリスナップ-ショットを取得し それを処理メモリデバッガに入れる Xcodeメモリデバッガはゲームの メモリ使用状況を直感的に把握できます ちょっとだけビューを見てみましょう 左側のデバッグ-ナビゲーターでは オブジェクトインスタンスの 階層的リストが表示されます
右側のファイルインスペクター ではメモリフットプリント 稼働時間 キャプチャーの日付など 有用な情報が表示されます
中央のエリアには 左から選択したオブジェクトと そのオブジェクトへの参照の接続状況を示す メモリグラフビューが表示されます このグラフについては後に説明します
また「ファイル」メニューでは メモリグラフを将来の分析用に 保存したりチームと簡単に共有したりできます
Macのゲームの場合leaksという コマンドラインプログラムで プロセスIDや名前を使いメモリ- グラフをキャプチャする事も可能 つまりゲームがフルスクリーンで 動作している場合 カーソルがゲームにフォーカス されたままになるように 安全なシェルでリモートで行うことができます メモリグラフ解析を始める為必要なものですね
Xcodeメモリデバッガと ターミナルの多機能コマンド- ラインツールを使ってこのメモリグラフを調べ 割当てやフットプリント等を確認しましょう まずメモリ使用状況を カテゴリー別に分類とよいでしょう フットプリントプログラムは まさにそれを実現します
フットプリントはメモリーグラフの情報を 使いこのハイレベルなサマリーを再現 一般的に まず大きなカテゴリーに 注目するとよいでしょう モダンレンダリングのサンプルコードに あるこのゲームのメモリグラフの場合 IOAcceleratorは通常最大のものです Sethが言ったようにMetalリソースも含まれます ここではヒープ割当てがいくつかの MALLOC-underscore-prefixedカテゴリに移動 システムがヒープ割当てをサイズプールに グループ化し性能を向上させるためです これらオブジェクトはサードパーティの プラグインやライブラリから来る可能性があり ゲームではサウンドエフェクトや 物理シミュレーションを行います
William “Cheer” Studioが制作した 素晴らしいAppleアーケードゲーム 『Manifold Garden』の メモリグラフを紹介します ゲームメモリの使用量表示を 許可してくれたのは嬉しいですね Unityを使ったManifold Gardenの ようなゲームエンジンや メモリマップの上にカスタム- アロケータを使用した場合 そのメモリは次のようにタグなし VM_ALLOCATEとして表示されます AppleのプラットフォームではApp固有のタグを 最大16個まで使用できるため メモリ使用量をより明確に 把握することができます 1行の変更で済むのでとても簡単です
まず16種類の選択肢からタグを作ります そしてem mapを呼び出す際にマイナス1を この新しいタグに置換え 「ファイル記述子」とします タグやカテゴリの定義については em mapのマニュアルページをご覧ください
mach VM allocateを使用する場合 アロケート時にflag引数に 同じフラグを含めてください
フットプリントプログラムの世界では ダーティサイズには スワップやコンプレッションも含まれるので 各カテゴリーでチャージされる 合計と考えればよいでしょう
以上が現在使用しているメモリの構成と そのフットプリントの簡単なイメージです メモリの一部は使用量が少なくなり 圧縮されたりスワップされたりする これらはメモリの節約に つながるかもしれません 次にゲームが使用している圧縮メモリや スワップメモリの量を調べ最適化することです
これではvmmapでメモリグラフを実行できます 2つのサイズを合わせたものではなく ダーティサイズとスワップサイズになります このダーティカラムには 現在スワップされていない または圧縮されていない 通常のダーティメモリが含まれ スワップされたカラムには圧縮または スワップされたメモリの 元のサイズが含まれています システムはこの2つの列を 足してフットプリントを決定 しかしスワップサイズの 欄の内容はそれほど頻繁に 使われるものではないので ゲームメモリを最適化するため 何を見るべきか良い指標となります ちなみにこちらがアロケーションサイズで 仮想サイズのカラムがあります そしてレジデントサイズには実行ファイルや メモリマップドファイルなど クリーンページが含まれます
便利なことにvmmapはヒープ割当てを 別テーブルで表示します vmmapの出力の下部にはヒープメモリを ゾーンごとにグループ分けして表示しています これらはゲームでの使用状況や ライフサイクルを反映します Mallocスタックログをオンにしたため ヒープ上のアロケーションは ツールのゾーンに入っています または2つのデフォルトゾーンになる: 割当てサイズに基づきMallocHelperZoneと DefaultMallocZoneになる またクォーツコアゾーンのような小さな システムユーティリティゾーンをスキップできます
数十 数百メガバイトといった 高い断片化サイズや割合で 示される断片化の疑いがある場合 WWDC 2021のセッションで 断片化の問題について詳しく説明されています
またダッシュダッシュサマリーなしで vmmapを実行するか vmmapを標準モードで使用すると これらのカテゴリ内の 各vm領域が1行ずつ表示されます 先ほど説明した仮想アドレス- 空間と同じようにです そこでvmmapを使えば活発に使用中のメモリから 使われていないダーティメモリを抽出できる また通常ゲーム内では様々な サイズの動的アロケーション つまりmallocされたヒープメモリの使用量も それなりにあるものです 特別な工夫が必要なのです ヒープツールはマックロックされた リソースをクラスごとに グループ化しインスタンス数でソートします これらのクラスはVTable Objective-C またはSwiftでThinkNC++で決定されます
いくつかのメタデータに関する ヘッダをスキップするため ダッシュダッシュクワイエット 引数を使用しています 今年新たにヒープがより賢くオブジェクトの 種類を識別するようになりました Mallocスタックログに記録された情報を利用し 呼び出し元や担当ライブラリを提示するため 巨大な非オブジェクトは過去のものとなる ここで再び『Manifold Garden』の メモリグラフを紹介します この例ではFMOD Studioなどのプラグインや GameAssembly.dylibなどの ゲームコンポーネントがどれだけ ヒープを消費しているかが 初めて明らかにされました これでメモリがどう広がるか 詳しく分かるようになりました そしてその情報を得るための 方向性を示しています この例では開発者は FMOD Studioを開いてゲーム内の サウンドトラックやエフェクトを微調整したり Unityでゲームコードの最適化を探したり といった具合に利用できます
クラスのインスタンス数 ではなくクラスの総サイズで ソートする方が便利な場合もあります モダンレンダリングのサンプル- プロジェクトのメモリグラフでは 2億5800万バイト以上を使用する クラスが上位を占めています モダンレンダリングサンプルで 大きなオブジェクトを探し続けるには ヒープを使用してオブジェクトを クラスの合計サイズ毎にダッシュでソートし 各クラスの要約ではなく ダッシュでサイズを表示する すべてのオブジェクトをリストアップします またバイトストレージには255万バイトの NSConcreteMutableDataの オブジェクトが1つあり: これは一見の価値がありそうです 次にその正体を探りましょう まずはアドレスを知りたいですね ダッシュダッシュのアドレスを追加しパターン NSConcreteMutableDataの後に ワイルドカードのドットスターを入力し ブラケットには10メガバイト 以上のオブジェクトのみを リストアップするサイズ- フィルタを入れています そのオブジェクトのアドレスがこちら 次のステップでより深く 分析するために使用します これがインスタンスのオブジェクト 識別が改良されたヒープツールです ここまでで理解するための 3つのツールを見てきましたが どれも異なる見解を示しています ゲーム内でどのオブジェクトが メモリを使っているのか 今回お見せしたのは あくまでワークフローの一例です ゲームに使用する記憶パターンや 技術によって 使い分けが可能です
存在がはっきりしないオブジェクトの 発見では次にその原点である アロケーション-コールスタックを 取得することになる
モダンレンダリングの 2億バイトのオブジェクトの場合 ダッシュでツリーモードを呼び出し malloc_historyにそのアドレスを渡しています 反転の引数を追加することで割り当てに 最も近い機能にフォーカスできます そして出来上がりです こちらがアロケーションのバックトレースです 同様にXcodeメモリ- デバッガでもオブジェクトの 割当て履歴がインスペクタに表示されます オブジェクトを選択しメモリインスペクタを クリックするだけでそこに表示されます 他の例としてアドレスの代わりにVM_ALLOCATEを クラスパターンとして渡すと ゲームやプラグインで 匿名のVM が使われているか どうかをチェックできます カスタムアロケーターを デバッグする場合などです Xcodeでもmalloc_historyでもアロケーションの バックトレースを知ることができ その行でブレークポイントを設定するなど より深く掘り下げるか判断することができます
最後にオブジェクトの参照に ついて調べることも有効です メモリグラフは様々な理由で Mallocスタックログが無効の場合でも 常にオブジェクトの参照を記録します 以前Xcode外でメモリグラフをキャプチャ するためリークを使用しました リークはさらにやってくれます メモリグラフ内のすべての 参照をチェックするため リークやリテインサイクルを 知ることができるのです リークはこのオブジェクトへの 参照ツリーをトレースツリー引数と ヒープからのオブジェクト- アドレスを使って取得します しかしこの例ではかなり大きなツリーなので ターミナルで表示するよりも 多少良い方法があります
Xcode14ではメモリグラフビューの デザインを変更し選択した オブジェクトの入力エッジと出力エッジ 両方を表示するようにしました
さらにXcodeに描画させたい エッジを選択するための 新しい隣接選択ポップオーバーもあります これにより複雑なゲーム状態での オブジェクト参照を 理解しようとする際の 生産性が大幅に向上します
少し探ってみると このオブジェクトに アクセスしているのは テクスチャマネージャである ことは間違いないようです ゲームではリークツールと メモリグラフビューを使用して 重要なオブジェクトの参照関係を 見つけこれらのオブジェクトが ゲーム内でどのようにアクセス されるか学ぶことを検討ください そこでリークやXcodeを使用してオブジェクトの 重要な参照を表示し見つける方法です これらのツールの詳しい使い方は リークのマニュアルページや Xcodeのヘルプをご覧ください
このメモリグラフ解析クックブックでは 各ステップでいくつかの 特定のツールを使用します すべてが協力してメモリー- グラフ上の解析を完成させる
要約するとメモリグラフでメモリを キャプチャして分析する場合は Mallocスタックログを有効にすることが第一です その後ゲーム用Xcodeで メモリグラフをキャプチャするか 代わりにMacゲーム用リークツールを使用します 次に大きなものやっかいなものを探します フットプリント vmmap ヒープツールは高レベルと詳細の 両方においてメモリの内訳を提供します malloc_history を使うとオブジェクトが どこに割当てられたか把握でき リークはオブジェクトの 使用状況や参照先を分析できます これまでのセッションでは 詳細なウォークスルーや これらのツールのより多くの 使い方のデモが含まれています これまでMetalリソースの探査は 先送りにしていました さて今がその時です Sethが説明してくれます 戻ってきました ゲームではMetalリソースが大きな メモリ使用になることがありますが そのメモリ使用量を最適化する方法があります
ここではゲーム内のMetal- リソースを最適化する際に 利用できるメモリ節約術をまとめました リソースの監査にメタルデバッガが どのように役立つかを見ていき さらにメモリを削減するための テクニックを学んでいきます
メタルデバッガーはゲームをデバッグ するためのワンストップショップで GPUフレームキャプチャを撮影すると サマリーページが表示されます これで取り込まれたワークロードに関する 一般的な統計情報を得られます
ページの下半分には4つのカテゴリーに分かれた インサイトのリストがあります 「メモリ」カテゴリのインサイトでは ゲームのメモリ節約を提案します 対処した後 数メガバイトの メモリを節約できますが このトレースには特別にメモリに 関する利点は多くありません
しかしあなたのゲームに特化した メモリ削減方法がある可能性があります Metalリソースが使用している メモリの全体像を把握するには 「メモリを表示」ボタンをクリックし メモリビューアを使用します
キャプチャしたリソースの一覧を表示できます 上半分はフィルタリングの為の 様々なカテゴリを示します テクスチャーなどリソースを 調べるのにすぐ使えます 下半分はテクスチャーだけを 表示したテーブルです とりあえずフィルターを外してみましょう リソーステーブルにはゲームを最適化 するためカラムが集められます そして興味深いリソースを素早く見つけるのに 役立ついくつかのコラムを 紹介したいと思います
インサイトの欄は先ほど サマリーページで見たものと同じです このカラムでテーブルをソートすると インサイトを持つ全リソースを すばやく表示できます またインサイトアイコンをクリックすると ポップオーバーで所見の説明と 可能なアクションが表示されます この欄の右隣にあるのが 「割り当てサイズ(Allocated Size)」です この欄でソートすることで 最大リソースを確認できます 一部のリソースが実際にメモリサイズを有効に 活用しているかチェックする のに役立つかもしれません 例えばテクスチャの解像度を 小さくしたりバッファに 読み込まれるモデルのポリ数を 少なくしてもゲームの ビジュアルクオリティに影響が ない場合はそうすることができます テクスチャーメモリを節約する 別の方法もありますので すぐに紹介します もう一つの興味深いカラムは 「Time Since Last Bound」です この列でリソースを並べ替えると最近 未使用のリソースが見つかります リソースが一度も使用されて いない場合 そのアセットを ロードする価値があるか否か 再確認するのも良いかもしれません 暫くバインドされていない リソースについては今後2度と 使用しない場合はリリースを 検討してもよいでしょう またパージ可能な状態を volatileに設定する事もできます Metalリソースは不揮発性 揮発性 空の 3つのパージ可能な状態のうちの1つである デフォルトではリソースは不揮発性である パージ可能な状態をvolatileに設定することで システム内のメモリ圧力が高い場合に Metalはメモリからリソースを 回避させることができる リソースが空になると システムはそのリソースを ゲームのフットプリントとして チャージしなくなります 再びリソースが必要になったらコンテンツが まだあるかどうかを確認し 必要なら再ロードします 使用頻度の低いリソースにのみ volatileを使用することを検討し パージ可能な状態が不利に 働かないようにします
以上がすべてのリソースに 対する一般的な注意点です 次はテクスチャについて 詳しく見ていきましょう メモリービューワのデフォルトでは すべての列が表示されるわけではないです テーブルのヘッダーを 右クリックするとテクスチャの ピクセルフォーマットのような カラムの表示・非表示が可能です ピクセルフォーマットを最適化で 節約できる量は異なる場合があります ゲーム内の多くのテクスチャは メモリ使用量と帯域幅を減らすため 16ビット半精度ピクセル形式を使用できます アルファ成分が1つのテクスチャが必要な場合 複数のカラーチャンネルを 避けることができます 最後に一部の読取り専用 テクスチャはブロック圧縮を 行うことでメモリ使用量を 削減できる場合があります ブロック圧縮されたピクセルフォーマットには ASTCやBCなどのオプションがあります さらにA15 Bionic以降テクスチャや レンダーターゲットに 非可逆圧縮を使用することで 可能な限り品質を維持したまま メモリを節約することができます 詳しくは過去の動画でご確認ください
これらはメモリビューアーを使用することで すぐに発見できるメモリ節約方法の一部です しかしさらにゲームを最適化するために いくつかのテクニックがあります テクスチャがシングルパス でしか使用されない場合 そのストレージモードを メモリレスに設定することで メモリと帯域幅を節約することができます メモリーレステクスチャは深度 ステンシル マルチサンプルテクスチャの ような一時的なレンダリングターゲットに 対して効果的に働きます それ以外の場合テクスチャが GPUにのみ使用される場合 そのストレージモードを プライベートまたはシェアード マネージドに設定できます 注意点としてApple Silicon Macでは iPhoneやiPadと同様に マネージドモードは必要ありません 以下はその例です このゲームにはDepth32Float_Stencil8 というテクスチャがあります 深度テクスチャはパスを またいで使用されますが ステンシルテクスチャの内容は破棄され フレーム内で後に使用されることはありません 代わりに2つのテクスチャを使い ステンシルテクスチャをメモリレスに することでメモリと帯域を 節約することができます
最後にゲーム内のメモリを有効活用 するためのテクニックとして もうひとつ興味深いものを紹介しましょう 同時に使用しない場合は ヒープからエイリアスされた リソースを使用することができます 同じアロケーションのメモリを 共有することができます しかしそれらのリソースへのアクセスを 同期させる時は特に注意が必要です ヒープから割り当てられた リソースの使用については 「Metal 3でバインドレスにする」 というトークで確認できます というわけでメモリ節約の チェックポイントは以上です このチェックリストがゲーム内の Metalリソースの診断に役立てば幸いです
ゲームメモリの最適化を目的とした メタルデバッガーの活用については WWDCで行われた他のトークをご覧ください Jackに戻します
ありがとう Seth 今日はガイドツアーに参加して ゲームのメモリ使用量を理解し 改善するためにできる多くの 興味深い事柄を探りました まずメモリフットプリントは ゲームのメモリ使用量を 把握するための主要な指標 でありダーティメモリに加えて 圧縮メモリとスワップメモリも含まれます そして強力なメモリデバッグ- ツールを体験しました Sethはインストゥルメントが 有用な遠隔測定トラックで メモリプロファイリングを強化する 方法を教えてくれました 新しい「ゲームメモリ」テンプレートは まさにこのために作られたものです その後メモリ状態のスナップショットを 保存するメモリグラフを紹介しました オブジェクト 参照割り当て履歴の メモリグラフを分析する柔軟で強力な コマンドラインプログラムがあります ヒープツールの改善とXcode- メモリデバッガーの再設計により ゲームメモリ解析が超高速化されます 最後にSethがMetalリソースの メモリ節約チェックリストと ゲームでのMetalリソースの使用に関する 質問にメタルデバッガが どのように答えるかについて説明しました また他のWWDCのセッションやドキュメント マニュアルページからも学ぶことができます
私達は皆さんのために最適で 柔軟なツールを進化させています 試さない理由はありませんよね? あなたが探しているものが 見つかるかもしれませんよ
またご意見ご感想がありましたら フィードバックアシスタントなどを 通じて遠慮なくお寄せください メモリの研究を楽しんでください ご視聴ありがとうございます
-
-
6:53 - Available memory for the process
#import <os/proc.h> API_UNAVAILABLE(macos) API_AVAILABLE(ios(13.0), tvos(13.0), watchos(6.0)) size_t os_proc_available_memory(void);
-
7:07 - Current and peak footprint
#if __has_include(<libproc.h>) #include <libproc.h> // On macOS. #else #include <sys/resource.h> // On iOS, iPadOS and tvOS. int proc_pid_rusage(int pid, int flavor, rusage_info_t *buffer) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0); #endif rusage_info_current rusage_payload; int ret = proc_pid_rusage(getpid(), RUSAGE_INFO_CURRENT, // I.e., new RUSAGE_INFO_V6 this year. (rusage_info_t *)&rusage_payload); NSCAssert(ret == 0, @"Could not get rusage: %i.", errno); // Look up in `man errno`. uint64_t footprint = rusage_payload.ri_phys_footprint; uint64_t footprint_peak = rusage_payload.ri_lifetime_max_phys_footprint;
-
10:04 - Record an Instruments trace
xctrace record --template "Game Memory" \ --attach ModernRenderer \ --output ModernRenderer.trace \ --time-limit 30s
-
10:14 - Record an Instruments trace, on a selected device
xctrace record --device-name "Seth's iPhone" \ --template "Game Memory" \ --attach ModernRenderer \ --output ModernRenderer.trace \ --time-limit 30s
-
16:52 - MallocStackLogging
# See `man malloc`. MallocStackLogging=lite # Live allocations only. MallocStackLogging=1 # All allocation and free history.
-
18:07 - Capture a memory graph
leaks $PID --outputGraph foo.memgraph # or leaks GameName --outputGraph foo.memgraph
-
20:12 - Tag mapped anonymous memory
size_t length; int tag = VM_MAKE_TAG(VM_MEMORY_APPLICATION_SPECIFIC_1); // Check out `man mmap`. void * reservation = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, tag, // Instead of using default `-1`. 0); if (reservation == MAP_FAILED) { @throw [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]; } return reservation;
-
20:30 - Tag anonymous memory
size_t page_count; mach_vm_size_t allocation_size = page_count * PAGE_SIZE; mach_vm_address_t vm_address; kern_return_t kr; kr = mach_vm_allocate(mach_task_self(), &vm_address, allocation_size, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_APPLICATION_SPECIFIC_1)); if (kr != KERN_SUCCESS) { // Refer to mach/kern_return.h. @throw [[NSError alloc] initWithDomain:NSMachErrorDomain code:kr userInfo:nil]; } return vm_address;
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。