ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Metal 3で、GPUバイナリをターゲットにして最適化する
オフラインのコンピレーションを使用して、プロジェクトビルド時にGPUバイナリを完全に生成する際に、App内のスタッター、初回起動の時間、新しいレベルでのロード時間を削減する方法をご覧ください。また、「サイズの最適化」のコンパイラオプションを使用して、大規模なGPUプログラムでのコンパイル時間やバイナリサイズの合計を最適化する方法も紹介します。
リソース
関連ビデオ
WWDC23
WWDC22
WWDC21
WWDC20
-
ダウンロード
♪ ♪ 皆さんこんにちは GPUソフトウェア部門 技術担当のGalo Avilaです このセッションではEylonと Metal3を使ってAppの GPUバイナリ生成を 改善する方法を紹介します オフラインコンパイルによる App内の処理時間 初回起動時間 ロード時間を 短縮する方法を説明します 次にコンパイラーオプション を使用しコード拡張変換を 調整しコンパイル時間を 向上する方法を説明します
オフラインコンパイルにより バイナリの生成を移行可能です MetalAppにもたらす 利点を理解するために 今GPUバイナリ生成できる 方法を確認しましょう バイナリの生成はビルド時と 実行時の両方で起きます 例えばソースからメタル- ライブラリをインスタンス化するとします これはAppの実行時 AppleのIntermediate Representation AIR に生成するものです これはCPUに高負荷の 作業ですがソースを Metalライブラリに プリコンパイルし 代わりにファイルから インスタンス化することで Appのビルド時に 移動させることができます ライブラリがメモリ上に あればステートと関数を含む Pipeline State Descriptorの 作成は軽い操作で済みます パイプライン状態- オブジェクトを作成するまで これは別のCPU集中型 操作になることがあり ジャストインタイムのGPU バイナリ生成が行われます
実行時のGPUバイナリ生成は CPUに負荷がかかるため Metalはパイプラインの状態 オブジェクト作成を高速化します PSOをインスタンス化 するときMetalは ファイルシステムの キャッシュにバイナリを保存します また新しいPSOを 作成するたびに 新しく生成された関数が 追加されます そのため以前に生成された バイナリが参照された場合は キャッシュから読み込まれる ことになります
Binary Archivesを使用し GPUバイナリがいつどこに キャッシュされるか明示的に 制御することができます PSOディスクリプタを 使うだけでアーカイブに GPUバイナリを何度でも キャッシュできます これでPSOの作成が軽い 操作で済むようになります バイナリアーカイブはより 柔軟なキャッシュが可能です ただ実行時に 生成する必要があります 多くの場合 ビルド時にアーカイブを 生成することが 望ましいのですが それがついに 可能になりました オフラインバイナリ生成では プロジェクトのビルド時に Metal pipelines scriptと 呼ばれる新しい artifactをMetalソースか Metalライブラリと共に指定します パイプラインスクリプトは APIにおけるパイプライン 記述子コレクションに相当 するcompiler toolchainです コンパイルプロセスの出力は バイナリアーカイブです App実行時 それ以上の GPUコード生成は起こりません 構築済バイナリアーカイブを 読むだけでPSO作成が加速します
オフラインコンパイルは CPUオーバーヘッドを削減し Appにメリットをもたらします さらに2つの方法でAppの 操作性を向上できます 初回起動や新しいレベルの ロード時間が劇的に速くなり 大きなエンゲージメントと インタラクションを得られます ランタイムコンパイルによる スタッターやフレームレート 低下をプリウォームフレーム メモリやCPUコストなしに 最終的に解消可能です 次はこれらメリットを より詳しく探って行きます
ここでは従来のAppラン- タイムバイナリ生成をします この例ではAppはロード 画面背後でGPUバイナリを コンパイルしてユーザーが Appを操作できるように なるまでの時間の 約3分の2を費やしています しかしオフラインコンパイル ではランタイムシェーダーの 生成はAppのビルド時に 移動しPSOの作成は わずかな時間で行われ ロード画面での待機も無く すぐにAppを使えます
オフラインコンパイルは スタッターの軽減にも有効です 従来のランタイムバイナリ生成では ロード時に作成する パイプラインの状態が 多すぎるため代わりに ジャストインタイムで何個か 作成することがあります その際コンパイルがコマンド エンコードを一時的に 中断する事によるコマ落ちが 発生することがあります オフラインコンパイルでは Appのビルド時に多くの シェーダをコンパイルでき 厄介なバブルを取り除けます 次にオフラインコンパイルの メリットを活用するための 開発者向けの新しい ワークフローを紹介します
オフラインでGPUバイナリ をビルドするための ツールチェイン新機能の 使用方法を学びます Pipelineスクリプトの入力 アーティファクトの生成方法を紹介します 次にGPUバイナリ生成の為の ツールチェインの呼び出し方についてです pipelines script artifactは 1つまたは複数の APIパイプラインの状態 記述子をJSON形式で 記述したもので様々な方法で 生成することができます 例えば好みのJSON エディタでのオーサリングや 開発テスト時に直列化された バイナリアーカイブから取得します 状態や関数を含むパイプライン 記述子と 相当するJSON表現を生成する Metalコードのスニペットを示します API metal library fileは libraries path propertyで示されます ディスクリプタの関数名を render pipelinesとします 最後にraster_sample_countや ピクセルフォーマットなど その他パイプラインの状態も script propertiesで取得されます JSONスキーマについては 開発者用資料を参照してください
JSONスクリプト生成を キックスタートさせる場合 Metalランタイムを 使用することが有効です 実行時にバイナリアーカイブを生成し 開発およびテストプロセスで 直列化するだけです ではMetal APIを使ってこれを 実現する方法を紹介します
ランタイムのデータ取得は ステートと関数を 含むパイプライン記述子を 作成しそれをアーカイブに 追加 するとGPUバイナリが生成されます それをファイルシステムに 直列化してAppの バンドルにインポートしたり Appからロードしたり することから始まります Metal 3ランタイムはバイナリと一緒に pipeline descriptorを保存します ではその抽出方法を紹介します metal-sourceは 既存のアーカイブから JSON pipelines scriptを 抽出することを可能にします バイナリ生成を実行時から 構築時に移行でき便利です flatbuffersと出力- ディレクトリオプションを つけてmetal-sourceを 起動するだけです その結果 pipelines script ファイルが生成され これを編集し追加のバイナリ を生成することができます ではツールチェインを 呼び出す方法を紹介します ビルドフェーズからGPUバイナリ を生成するのは簡単です ターミナルからソースと pipelines scriptと 出力ファイルを指定して metalを呼び出すだけです 出力されたメタルライブラリ はGPUバイナリを含みツール- チェーンがサポートする デバイスに展開できます またソースの代わりに Metalのライブラリがあれば それもツールチェインに 渡すことができます 既存のMetalライブラリから バイナリを生成するのも Metal translator toolを 使えば簡単です ターミナルでソースと pipelines scriptと 出力ファイルを指定して metal-ttを呼び出すだけです 結果Metalライブラリには ツールチェーンでサポートされた 全デバイスのバイナリが 含まれるようになります オフラインでのバイナリ 作成方法がわかったところで バイナリのロード方法を おさらいしておきます アーカイブディスクリプタの 作成時に バイナリURLを指定し それを使い アーカイブをインスタンス化します それだけです MetalのバイナリアーカイブAPI については 過去のトークをご参照ください 最後にオフラインで生成された 成果物に対する GPUバイナリの互換性をMetalが どう処理するかのメモです オフラインで生成された バイナリは将来の OSバージョンや製品との 互換性を確保するため OSアップデート時や Appのインストール時に バイナリアーカイブを 柔軟にアップグレードします これは非同期で バックグラウンドで行われます 実行時のスタッタを取り除き 初回起動や新しいレベルの ロード時間を短縮するオフライン コンパイルの利点をご活用ください これら改善は利用者にも 見えるものであり App全体の利便性を 高めることができます さて次はEylonです ありがとう Galo 次に新しいコンパイル オプションである サイズの最適化を 紹介します Metalコンパイラは実行時の性能向上のために コードを最適化します 最適化によってGPUの プログラムサイズが拡大し 予期せぬコストが 発生する場合があります 例えば関数のインライン化は 関数呼び出しの オーバーヘッドを回避する ための最適化です 呼び出し先に関数の本体を インライン化し動作します この例のカーネルはコードは 多くないように見えますが インライン化した後です 関数'f'と'g'のコピーを含み また'f'と'g'から呼ばれる 関数ヘルパーや 非プリミティブライブラリ- 関数なども含む 可能性があります
もう一つの最適化は ループアンロールで ループ本体のコピーをさらに インライン化することで 反復処理に渡る 並列性を確保し 分岐のオーバーヘッドを 回避します コンパイラは2回のループの 反復をアンロールすることも 固定境界を持つループの 全反復にも対応可能です 最適化によって大きな プログラムが作られると コンパイラもコンパイルに 時間がかかり状況によっては コストを避けることが 望ましい場合もあります Xcode14では新しくMetalに サイズの最適化モードを導入しました このモードではコンパイラが 最適化を行う際に インライン化や ループのアンロールなどの サイズ拡大変換を制限します デフォルトの最適化では コストがかかりすぎる場合に GPUバイナリを小さくし コンパイル時間の短縮を 目的としています サイズを最適化した場合 結果的に実行性能が 低下する可能性があります 実際にそうなるかは プログラムにより異なるので 両方の最適化モードを試して 比較する必要があります この機能は すべてのシェーダーに対してサイズと コンパイル時間を改善できない可能せもあり インライン化やアンロール化が 一般的な深い呼び出しパスや ループを持つ大規模な プログラムに対して 最も効果が期待できます このオプションはデフォルト 最適化で予想外に長い コンパイル時間に遭遇した時 試してみる価値があります ビルド時または実行時に コンパイルする場合に利用可能です サイズの最適化が違いを もたらすケースを紹介します Cyclesは3Dデザイン環境 Blender製品版レンダラーを 実装したオープンソース プロジェクトで最近 Metalをサポートするため アップデートされました Appleは最近Blender 開発基金に参加しましたが その中でわかったのは Blenderパスの パストレーシングのアルゴリズムは 多くのヘルパー関数とループを持つ 大規模な計算シェーダーを使用し コンパイル時間は 数分に及ぶということでした これらシェーダーはMetal3の 新しいオプションである サイズの最適化の恩恵を 受けると判明しました
シーンをApple Silicon GPUで レンダリングする際 最適化を有効にすることで シェーダ-パイプラインのコンパイルを 含むBlenderの準備時間が 最大1.4倍改善されました レンダリング速度による性能低下は ほぼなく スピードアップを実現しました レンダリング速度が 最大4%低下するものもあれば 全く低下しないものも ありました そのためランタイム性能が 低下する可能性はあります しかし 最適化することで 実行時の性能向上も可能です こちらに例を用意しました Intel GPUでサイズの 最適化を有効にした場合の 同じシーンのレンダリング 時間の高速化です その結果コンパイルが 速くなっただけでなく レンダリングも最大で 1.6倍まで速くなりました どのようにして? 小さいGPUプログラムなら サイズが大きい時に発生する ランタイムペナルティをある程度 回避出来るからです 命令キャッシュミスの減少や 必要なレジスタの減少 メモリへの流出の減少 さらにはより 多くのスレッドを 並列に実行することが できます 全てのシェーダーやシーンで 再現される訳でない事 性能低下が起こる可能性が あることをご了承ください コンパイル時と実行時の 両方のパフォーマンスに 対する実際の影響を 評価する必要があります Metalソースから コンパイルする際 3つの環境で サイズの最適化を 有効にすることができます Xcode14のユーザー インターフェイスでは ビルド設定で サイズの最適化を指定します 「Metal Compiler」の 「Build Options」で 「Optimization Level」の 設定を確認します 「Default」レベルでは パフォーマンスを最適化し 「Size」は サイズを最適化します
コマンドラインでMetalソースを コンパイルする場合 オプション-「Os」でサイズを 最適化するよう指定します 例1はコンパイル&リンク- コマンドにオプションを指定 例2ではコンパイル- コマンドを2つ用意し そのうちの1つだけに オプションを指定して 一部のシェーダーに対して のみ有効にしています このオプションは リンクコマンドや それ以降のコマンドに 渡す必要はありません またこのトークで紹介した コマンドを使用しGPU- バイナリを生成の有無を問わず サイズの最適化を使用可能です
最後にApp実行時に newLibraryWithSourceなど Metal Framework APIで Metalソースをコンパイルする場合 optimizationLevelプロパティを 使って MTLCompileOptionsオブジェクトで サイズの最適化を指定できます 最適化レベルは「default」 または「size」になります 皆さんが新しい 最適化モードの 恩恵を受けることを 期待しています まとめです まず オフラインのコンパイルを紹介しました GPUバイナリを完全に 生成する新ワークフローで App内のスタッタを減少させ 初回起動とロード時間を短縮します 続いてソースのコンパイル時に プログラムサイズと コンパイル時間を削減する Metalの新しい最適化モード optimize for sizeを(サイズの最適化) を紹介しました これらの改善により 皆さんのAppやゲームが より良いユーザー体験を 提供できる事を願っています コンパイルコスト削減で セットアップやロードの短縮 スタッタの減少 これが 新しいワークフローを実現します ご視聴ありがとうございます
-
-
4:47 - Using a JSON editor: render pipeline descriptor
// An existing Obj-C render pipeline descriptor NSError *error = nil; id<MTLDevice> device = MTLCreateSystemDefaultDevice(); id<MTLLibrary> library = [device newLibraryWithFile:@"default.metallib" error:&error]; MTLRenderPipelineDescriptor *desc = [MTLRenderPipelineDescriptor new]; desc.vertexFunction = [library newFunctionWithName:@"vert_main"]; desc.fragmentFunction = [library newFunctionWithName:@"frag_main"]; desc.rasterSampleCount = 2; desc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; desc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float;
-
4:47 - Using a JSON editor: pipelines script
{ "//comment": "Its equivalent new JSON script", "libraries": { "paths": [ { "path": "default.metallib" } ] }, "pipelines": { "render_pipelines": [ { "vertex_function": "vert_main", "fragment_function": "frag_main", "raster_sample_count": 2, "color_attachments": [ { "pixel_format": "BGRA8Unorm" }, ], "depth_attachment_pixel_format": "Depth32Float" } ] } }
-
5:33 - Harvesting sample
// Create pipeline descriptor MTLRenderPipelineDescriptor *pipeline_desc = [MTLRenderPipelineDescriptor new]; pipeline_desc.vertexFunction = [library newFunctionWithName:@"vert_main"]; pipeline_desc.fragmentFunction = [library newFunctionWithName:@"frag_main"]; pipeline_desc.rasterSampleCount = 2; pipeline_desc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; pipeline_desc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float; // Add pipeline descriptor to new archive MTLBinaryArchiveDescriptor* archive_desc = [MTLBinaryArchiveDescriptor new]; id<MTLBinaryArchive> archive = [device newBinaryArchiveWithDescriptor:archive_desc error:&error]; bool success = [archive addRenderPipelineFunctionsWithDescriptor:pipeline_desc error:&error]; // Serialize archive to file system NSURL *url = [NSURL fileURLWithPath:@"harvested-binaryArchive.metallib"]; success = [archive serializeToURL:url error:&error];
-
6:01 - Extracting a JSON script
metal-source -flatbuffers=json harvested-binaryArchive.metallib -o /tmp/descriptors.mtlp-json
-
6:24 - Generate a GPU binary from source
metal shaders.metal -N descriptors.mtlp-json -o archive.metallib
-
6:48 - Generate a GPU binary from Metal library
metal-tt shaders.metallib descriptors.mtlp-json -o archive.metallib
-
7:07 - Load GPU binaries via the runtime API
MTLBinaryArchiveDescriptor *desc = [MTLBinaryArchiveDescriptor new]; desc.url = [NSURL fileURLWithPath:@"archive.metallib"]; NSError *error = nil; id<MTLDevice> device = MTLCreateSystemDefaultDevice(); id<MTLBinaryArchive> binaryArchive = [device newBinaryArchiveWithDescriptor:desc error:&error];
-
12:11 - Enable optimize for size in command lines
xcrun metal -Os large_shader.metal # or xcrun metal -c -Os large_shader.metal xcrun metal -c more_shaders.metal xcrun metal large_shader.air more_shaders.air
-
12:44 - Enable optimize for size with Metal framework
MTLCompileOptions* options = [MTLCompileOptions new]; options.optimizationLevel = MTLLibraryOptimizationLevelSize; NSString* source = @"..."; NSError* error = nil; id<MTLLibrary> lib = [device newLibraryWithSource:source options:options error:&error];
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。