Xamarin.Mac の Ahead-Of-Time (AOT) コンパイル

概要

Ahead-Of-Time (AOT) コンパイルは、スタートアップ パフォーマンスを向上させる強力な最適化手法です。 ただし、ビルド時間、アプリケーション サイズ、プログラムの実行にも大きな影響を与えます。 これによって課されるトレードオフを理解するために、アプリケーションのコンパイルと実行について少し詳しく説明します。

C# や F# などのマネージド言語で記述されたコードは、IL と呼ばれる中間表現にコンパイルされます。 ライブラリおよびプログラム アセンブリに格納されているこの IL は、比較的コンパクトで、プロセッサ アーキテクチャ間で移植できます。 ただし、IL は中間的な命令セットに過ぎず、ある時点で IL をプロセッサに固有のマシン コードに変換する必要があります。

この処理は、次の 2 つのポイントで実行できます。

  • Just-In-Time (JIT) – アプリケーションの起動中と実行中に、IL はメモリ内でマシン コードにコンパイルされます。
  • Ahead-Of-Time (AOT) – ビルド中に IL がコンパイルされ、ネイティブ ライブラリに書き出され、アプリケーション バンドル内に格納されます。

各オプションには、次のような利点とトレードオフがあります。

  • JIT
    • 起動時間 – JIT コンパイルは起動時に行う必要があります。 ほとんどのアプリケーションでは、これは 100 ミリ秒単位ですが、大規模なアプリケーションの場合、この時間は大幅に増加する可能性があります。
    • 実行 – JIT コードは使用する特定のプロセッサに合わせて最適化できるため、少し優れたコードを生成できます。 ほとんどのアプリケーションでは、これは最大で数パーセント程度速くなります。
  • AOT
    • 起動時間 – 事前コンパイル済みの dylib の読み込みは、JIT アセンブリよりも大幅に高速です。
    • ディスク領域 – ただし、これらの dylib には大量のディスク領域が必要な場合があります。 どのアセンブリが AOT されているかに応じて、アプリケーションのコード部分のサイズを 2 倍以上にすることができます。
    • ビルド時間 – AOT コンパイルは JIT よりもかなり遅いため、それを使用したビルドは遅くなります。 この速度低下の範囲は、コンパイルされるアセンブリのサイズと数に応じて、数秒から 1 分以上に及ぶ可能性があります。
    • 難読化 –マシン語コードよりも大幅にリバース エンジニアリングが容易な IL は必ずしも必要ではないため、機密コードを難読化するために除去できます。 これには、以下で説明する "ハイブリッド" オプションが必要です。

AOT の有効化

AOT オプションは、今後の更新で Mac の [ビルド] ペインに追加される予定です。 それまでは、AOT を有効にするには、Mac Build の “追加の mmp 引数” フィールドを介してコマンド ライン引数を渡す必要があります。 オプションは次のとおりです。

--aot[=VALUE]          Specify assemblies that should be AOT compiled
                          - none - No AOT (default)
                          - all - Every assembly in MonoBundle
                          - core - Xamarin.Mac, System, mscorlib
                          - sdk - Xamarin.Mac.dll and BCL assemblies
                          - |hybrid after option enables hybrid AOT which
                          allows IL stripping but is slower (only valid
                          for 'all')
                          - Individual files can be included for AOT via +
                          FileName.dll and excluded via -FileName.dll

                          Examples:
                            --aot:all,-MyAssembly.dll
                            --aot:core,+MyOtherAssembly.dll,-mscorlib.dll

ハイブリッド AOT

macOS アプリケーションの実行中、ランタイムは、AOT コンパイルによって生成されたネイティブ ライブラリから読み込まれたマシン語コードを既定で使用します。 ただし、トランポリンなど、JIT コンパイルで大幅に最適化できるコード領域もあります。 そのためには、マネージド アセンブリ IL を使用できる必要があります。 iOS では、アプリケーションは JIT コンパイルの使用を制限されています。コードのこれらのセクションも AOT コンパイルされます。

ハイブリッド オプションは、これらのセクション (iOS など) をコンパイルするだけでなく、IL が実行時に使用できなくなると仮定します。 その後、この IL はビルド後に除去できます。 前述のように、ランタイムは、一部の場所であまり最適化されていないルーチンを使用せざるを得ない場合があります。

その他の考慮事項

AOT のスケーリングによる負の影響は、処理されるアセンブリのサイズや数によって大きくなります。 たとえば、完全なターゲット フレームワークには、最新型よりもかなり大きな基本クラス ライブラリ (BCL) が含まれているため、AOT の実行に大幅に時間がかかり、より大きなバンドルが生成されます。 これは、フルターゲット フレームワークが未使用のコードを除去するリンクと互換性がないことで、さらに悪化します。 最良の結果を得るには、アプリケーションを最新型に移行し、リンクを有効にすることを検討してください。

AOT のもう 1 つの利点は、ネイティブ デバッグとプロファイリング ツールチェーンとの相互作用が改善されたことです。 コードベースの大部分は事前に Ahead-Of-Time コンパイルされるため、ネイティブ クラッシュ レポート、プロファイリング、デバッグ内に読みやすい関数名とシンボルができます。 JIT で生成された関数にはこのような名前がないため、多くの場合、解決が非常に困難な名前のない 16 進数オフセットとして表示されます。