Unity を検索

Burst 1.7 でゲームの品質を向上させる

2022年3月14日 カテゴリ: Engine & platform | 9 分 で読めます
Hardware Header
Hardware Header
取り上げているトピック
シェア

Is this article helpful for you?

Thank you for your feedback!

Burst パッケージの最新バージョンでは、イテレーションにかかる時間と Burst インスペクターの両方に素晴らしい改良が加えられています。この記事では、何が変わったのか、そして Unity のハイパフォーマンス C#(HPC#)コンパイラー技術により、すべてのプラットフォームでより簡単にパフォーマンスを向上させることができるようになったその仕組みについて見ていきます。

Unity の DOTS 技術スタックは、Burst を活用して高度に最適化されたコードを提供します。Burst はスタンドアロンパッケージで、Unity 2019.4 以降のパッケージマネージャーで利用可能です。主要なデスクトップ、コンソール機、モバイルの各プラットフォーム向けに、すでに数千のプロジェクトが Burst を活用しています。

 

イテレーション時間を改善するための取り組み

これまでの Burst のリリースで、Burst を使った毎日の開発体験を改善するための重要な 改善を行ってきました。Burst 1.7 でもその流れを引き継ぎ、イテレーション時間の短縮に注力しました。イテレーション時間とは何かC# スクリプトに変更を加え、エディターに戻り、スクリプトのコンパイルが終了するのを待ち、Burst のコンパイルが終了するのを待ち、再生モードに入って変更をテストするという、開発の「内部ループ」のことを考えてみましょう。

Burst 1.7 では、Burst の処理のために生じる待ち時間を劇的に短縮しました。Burst コンパイルは、パイプラインの早い段階(スクリプトをコンパイルするパイプラインが .NET アセンブリのコンパイルを終了した直後)で行われるようになり、多くの場合、結果のコードを実行する必要が生じるまでに終了するようになりました。以前のバージョンの Burst のように、それぞれの Burst エントリポイントを別々にコンパイルするのではなく、Burst エントリポイント(ジョブや関数ポインターなど)を一括でコンパイルするようにし、コンパイラーのスループットを向上させ、エディターがロードする必要のあるライブラリの数を減らしました。

Burst 1.7 では、直接呼び出しのパフォーマンスも大幅に改善されました。直接呼び出しは Burst 1.5 で追加された機能で、マネージド C# コードが BurstCompiler.CompileFunctionPointer を介さずに Burst コンパイルされたメソッドを直接呼び出すことを許可するものです。ドメインリロード時には、直接呼び出しのメソッドを配線するための初期化作業が必要ですが、Burst 1.7 では、この初期化作業を最大 33 倍高速化することに成功しました。

今回のイテレーション時間に関する最後の話題として、SharedStatic の初期化のコストについて調べました。SharedStatic は、マネージド C# と HPC# の間でデータの共有を可能にする仕組みです。Burst 1.7 では、SharedStatic の初期化を最大で 13 倍高速化しました。 

下のグラフは、Burst 1.6 と比較して、Burst 1.7 でのパフォーマンス向上を示したものです。この測定は、顧客の大規模プロジェクトで行われました。最初のグラフは、ストップウォッチ(System.Diagnostics.Stopwatch ではなく、実際のストップウォッチ)を使ってエディターを観察したもので、実際に使っているときに期待できる改善を反映した値になっているはずです。

Burst 1.7 performance improvements

下の 2 つ目のグラフは、Burst だけに注目して、エディターで発生しうる他のものは除外したデータを示しています。このプロジェクトと変更されたファイルに限られてはいますが、以下の 3 つのタイミングすべてにおいて、Burst 1.7 は Burst 1.6 より高速です。

  • コールドキャッシュ:Burst がプロジェクト内のコードのコンパイル結果をまだキャッシュしていないタイミング。
  • ウォームキャッシュ:Burst がプロジェクトのコードをすでにコンパイルしており、キャッシュされたコンパイル結果をディスクから読み込む必要があるタイミング。
  • ファイルを 1 つ変更:あるファイルが変更された後、Burst がどのエントリポイントを再コンパイルする必要があるかを確認し、必要と判断されたものをコンパイルするタイミング。Burst 1.7 での改善幅は一般的にどのファイルを変更するかに依存することに注意してください。たとえば、すべての Burst エントリポイントで使用されているメソッドを変更すると、Burst 1.6 と Burst 1.7 の差は小さくなります。この例では、エントリポイントのメソッド自体を変更しました。
Burst 1.7 performance improvements 2

Burst インスペクター

Burst インスペクター(Jobs > Burst > Open Inspector... メニューからアクセス可能)は、最適化作業に非常に便利なツールです。このツールでは、ターゲット CPU で実行されるアセンブリコードを表示することができます。Burst 1.7 では、ご要望の多かったいくつかの機能を追加しました。いろいろと言葉を連ねるより、スクリーンショットを見ていただいたほうが早いでしょう。

Burst Inspector branch markers
分岐マーカーを表示した Burst インスペクターのスクリーンショット。

ご覧のように、分岐マーカーを追加して、コードの実行パスを視覚化しやすくしています。なお、分岐マーカーは「Show Branch Flow」チェックボックスでオフにできるので、不要なときでも邪魔にはなりません。この機能の特に良いところは、分岐フローの矢印をクリックすると、このように矢印の反対側にジャンプすることです。

分岐マーカーをクリックして分岐先にジャンプする例

逆アセンブルの重要度の低いブロック(ディレクティブや定数データなど)は自動的に折りたたまれるようになりましたが、これらは表示したいときにトグルで切り替えることが可能です。

また、Burst 1.7 では、分解した部分のみを選択してコピーする機能が新たに追加されました。

分解した特定の部分を選択してコピーする例

 

その他の改善点

以下は、Burst 1.7 における、小さいながら重要な改良点のリストです。

  • Arm Neon vst1* の API に完全対応しました。Burst 1.6 でこれらの API は追加されましたが、実験的であったことから、#define でガードしていました。Burst 1.7 では、この #define によるガードがなくなり、完全にサポートされるようになりました。
  • Burst を使ったコード内で System.Span と System.ReadOnlySpan がサポートされるようになりました。これらの型はエントリポイント引数として許可されません。
  • Burst は、デフォルトで LLVM Version 12.0.0 を使用するようになり、LLVM プロジェクトの最新の最適化に関する改良を利用できるようになりました。
  • LLVM の最適化パイプラインを変更し、ループベクタライザーの後にループアンローラーを排他的に実行するようにしました。これによって、多くの場合、コード生成の品質が改善されます。
  • fmod と浮動小数点モジュールでより高速なアルゴリズムを使用するようにし、パフォーマンスを向上させました。
  • Burst は、静的コンストラクターを利用しているとき、実行時にシンボルが欠落する IL ストリッピングを回避するために link.xml を自動的に生成するようになりました。
  • ロード命令やストア命令をメモリ移動命令に安全に変換できるケースをより多く検出することで、大規模な構造体のコピーを行う際のコンパイラーの性能を向上させました。
  • Burst メニューの「Show Timings」オプションを有効にしたときのタイミング表示方法を変更しました。情報が整理され、よりわかりやすい方法で表現されるようになっています。

Burst の今後の展開

Burst 1.7 は Unity 2019.4 に対応する最後のバージョンです。次の Burst バージョンでは、Unity 2020.3 が最低要件となります。 ご意見、ご質問があるとき、あるいは単に Burst で行っていることをシェアしたいというときも、Burst フォーラムに気軽にメッセージを残してください。

2022年3月14日 カテゴリ: Engine & platform | 9 分 で読めます

Is this article helpful for you?

Thank you for your feedback!

取り上げているトピック