IL2CPPにおけるAggressiveInliningの効果
C#ではメソッドに[MethodImpl(MethodImplOptions.AggressiveInlining)]を付けると、JITコンパイラにそのメソッドをできるだけインライン化するようにお願いできます。
しかしUnityでIL2CPPビルドするとJITコンパイラは使われません。そこでAggressiveInliningはどう扱われるのか調べてみました。
実際にビルドされたC++のコードを確認してみるとAggressiveInliningを付けたメソッドにはC++のinlineが付けられています。
さらに調べてみるとこの動作について説明しているページを見つけました。色々と参考になったので以下に翻訳します。
IL2CPPがC++コードを生成する際、ジェネリックメソッドやジェネリッククラス内のメソッドはそれぞれGenericMethod??.cppやGenerics??.cppというファイルに配置されます。(ここで??は任意の番号を表します。IL2CPPは番号違いで多くのcppファイルを生成します)非ジェネリックのメソッドはAssembly-CSharp??.cppのようなアセンブリ名に基づくファイルに配置されます。
デフォルトではC++コンパイラは2つのcppファイルにまたがってインライン化できません。IL2CPPにおいては[MethodImpl(MethodImplOptions.AggressiveInlining)]をつけたメソッドはそのメソッドが使われているそれぞれのcppファイルに複製されます。これでC++コンパイラがそのメソッドをインライン化できるようになります。
Windows Standalone向けビルドでビルド設定を”Master”にしてコンパイルするとリンク時最適化が有効になります。これによってC++コンパイラはリンク時までインライン化の判断を遅延できます。リンカはプログラム全体を知っているので複数のcppファイルをまたがって最適化できます。なので実行時に最高のパフォーマンスを得るために”Master”設定を使うのは一般にいい考えです。
この説明を読むとC#におけるAggressiveInliningよりも広い効果がありそうです。特に別アセンブリから呼ばれるpublicメソッドやジェネリック内のメソッドに付けると利きそうです。そしてそれよりもビルド設定を”Master”にすることで手軽に速くなりそうです。
ついでにIL2CPPはAggressiveInliningだけでなくMethodImplOptions.NoInliningやMethodImplOptions.NoOptimizationにも対応しているようです。Windows向けビルドにおいてはNoInliningが__declspec(noinline)、NoOptimizationが__pragma(optimize(“”, off))にそれぞれ変換されていました。こちらはあまり使うことはなさそうです。
AggressiveInliningは思っていたよりも効果がありそうなので積極的に使いたいと思います。