Unity を検索

機能プレビュー:Unity 2022.1 ベータ版の IL2CPP Full Generic Sharing

2021年12月15日 カテゴリ: Engine & platform | 12 分 で読めます
Beta feature preview header
Beta feature preview header
シェア

Is this article helpful for you?

Thank you for your feedback!

Full Generic Sharing により、より表現力が豊かで、よりテストしやすいコードを書くことができます。これにより、実行時に検出可能なスクリプトエラーがなくなるだけでなく、モバイルデバイスやコンソール機などのプラットフォーム上のコードの挙動がより予測しやすくなります。読み進めて、そのしくみを学びましょう。

どのようなものか

ジェネリックは、C# の強力な機能です。これにより、型から独立した形で動作をコードで表現することができます。開発者としては、List<string> が List<int> や List<T>(T は任意の型)のように動作することを期待しています。

IL2CPP は長年、T が参照型(文字列、オブジェクトなど)である場合に、ジェネリック共有を使用してきました。これは C# の参照型が常にポインターで表されるためにうまく動作します。List<string> のサイズと実装は List<object> のサイズと実装と一致することになるからです。しかし、64 ビットシステム(ポインターは 8 バイト)で T が int(4 バイト)の場合はどうなるのでしょうか。IL2CPP では、List<int>、List<double>、List<MyValueType> などに対して特別なコードを生成する必要があります。

そのため、Unity 2022.1 では、IL2CPP はすでに任意の T、参照型、または値型用の List<T> を処理できる特別なコードを生成するようになっています。この技術は Full Generic Sharing と呼ばれています。

これが解決する問題

仮想ジェネリックメソッドは C# の表現力豊かな機能で、ジャストインタイム(JIT)コンパイルと相性が良いのですが、IL2CPP のようなアヘッドオブタイム(AOT)コンパイルの場合には、実装が困難です。そこで登場したのが、Full Generic Sharing です。

Unity マニュアルから、一般的な仮想メソッドの例を見てみましょう。

このコードは、仮想ジェネリックメソッドの表現力を示すものです。言い換えると、IManager インタフェースを実装するクラスから、IReceiver インタフェースを実装したクラスに、任意の型のデータ(「メッセージ」)を送ることができるのです。Unity2021.2 の IL2CPP では、この一見単純なコードが動作しません。実行時、プレイヤーログに以下のエラーが表示されます。

ExecutionEngineException: Attempting to call method 'Test::OnMessage' for which no ahead of time (AOT) code was generated.   Consider increasing the --generic-virtual-method-iterations=1 argument
  at Manager.SendMessage[T] (IReceiver target, T value) [0x00000] in <00000000000000000000000000000000>:0 
  at Test.Start () [0x00000] in <00000000000000000000000000000000>:0

このエラーを紐解いてみましょう。 

Start メソッドの Send Message の呼び出しはインターフェース(IManager、仮想ジェネリックの「仮想」部分)を通して行われるため、IL2CPP はコードをコンパイルする際に、どのメソッドが実行時に呼び出されるかを検出しません。

これは不思議に見えるかもしれません。なぜ IL2CPP はこのことを理解できないのでしょう。実際には可能なのです。IL2CPP は、コンパイル時に利用可能なすべてのコードを検索し、この呼び出しが終了する可能性がある場所を決定することが可能です。しかし、この検索は高価でプロジェクトがビルドされるまでの待ち時間が発生します。また、IL2CPP が決して呼び出されることのない余分なコードを生成して、最終的な実行ファイルのサイズを大きくしてしまう可能性があります。 

エラーメッセージにある --generic-virtual-method-iterations 引数は、IL2CPP が検索に使える時間を指定するためのものです。JIT コンパイラにとって、このような仮想ジェネリックメソッドの呼び出しは実にわかりやすいものです。実行時に対象のメソッドを「見て」、適切な処理を行うことができるのです。Unity2022.1 で、IL2CPP も同じような仕掛けを覚えました。これにより、新しい特別なバージョンの SendMessage(ここでは「完全共有」バージョンと呼びます)が生成されるようになりました。

T、参照型、値型のどれを指定したかに関係なく、これは動作します。つまり、IL2CPP がコンパイル時に対象のメソッドが何であるかを確認できない場合、代わりにこの完全共有バージョンを呼び出すことになります。C# のコードも同様に表現力があり、実行時に動作し、コンパイルも高速です。

詳細を見る

Full Generic Sharing 技術は、AOT プラットフォーム上のコードを JIT プラットフォーム上のコードに近い形で動作させることができるという点で非常に有用です。これにより、実行時に初めてトラブルに出会う可能性を小さくすることができます。

この ExecutionEngineException エラーは、他のケースでも発生することが判明しました。IL2CPP が実行するコードの判断に失敗するたびに、このエラーが発生することがあります。シリアライザーでは、新しいシリアライズデータが IL2CPP が推測できない型にデシリアライズされることがよくあります。しかし、Unity 2022.1 では、IL2CPP が ExecutionEngineException を発生させることはなくなり、修正が難しい類のエラーを一掃することができます。

ただし、コードによっては、入れ子状の再帰的なジェネリック型を使用することも考慮してください。IL2CPP はコンパイル時にこれらの型を無限に処理し続けることができるため、ビルド処理にかかる時間に制限を設ける必要があるのです。

IL2CPP では、実行時に深く入れ子状になった型が必要になった場合、次のようなエラーが発生することがありました:

「IL2CPP encountered a managed type that it cannot convert ahead of time. The type uses generic or array types, which are nested beyond the maximum depth that can be converted.」

Full Generic Sharing が導入されたことで、IL2CPP が絶対にエラーにならない実装を使用するようになるため、このエラーメッセージに遭遇することはなくなりました。

サイズを変更して、できるだけ小さくしたいプロジェクトがあるとします。List<int>、List<double>、List<string> の実行可能なコードがあるかもしれませんが、あまりにいろいろな実装があるので、実行ファイルのサイズとのバランスを再考したほうがよいでしょう。 

あらゆる List<T> に対して、1 つの完全に共有された汎用的な実装があれば素晴らしいと思いませんか?そこで、Player Settings の IL2CPP Code Generation のところにある「Faster (smaller) builds」オプションをチェックしてみてください。チェックすると、Full Generic Sharing を活用し、最小限の実行コードで最短のビルド時間を実現します。もちろん、インクリメンタルビルドを迅速に行うことも可能です。プロジェクトで List<DateTime>(または他の T)を使用することにした場合、IL2CPP はその実装のために新しいコードを生成したりコンパイルしたりする必要がなくなります。

ベータ版の利用を始める

IL2CPP Full Generic Sharing を活用したコードを書き始めるには、Unity 2022.1 ベータ版を Unity Hub またはダウンロードページからダウンロードしてください。ベータ版は本制作段階のプロジェクトでの使用を想定していないことにご注意ください。お使いになる前に必ず既存のプロジェクトのバックアップを取ってください。

Unity 2022.1 が皆さんのお手元で上手く動いているか、ぜひお知らせください。ベータ版フォーラムで、ご意見をお聞かせください。Full Generic Sharing や、その他の機能に関しても、現在ご使用中の皆様からのご意見・ご感想をお待ちしております。再現性のあるオリジナルのバグのレポートを提出していただいた方を対象に、懸賞をご用意しております。提出を重ねていただくほど、当選する可能性が高まっていきます。詳細は、ベータ版リリースのブログ記事をご覧ください。

2021年12月15日 カテゴリ: Engine & platform | 12 分 で読めます

Is this article helpful for you?

Thank you for your feedback!