Xamarin
コンパイラ
Xamarin.iOS
aot
Xamarin.Mac

「Ahead of Time Compilation with Xamarin.Mac by Chris Hamons氏(Xamarin.Mac Lead)」の日本語訳

前置き

  • Xamarin.Macのコンパイルの話を調べていたら、Xamarin.Mac LeadのChris Hamons氏の記事を発見
  • iOSと同様AOTしかできないのでは?と思ってたらそうではなく、AOTを「オプション」として実装したときの話が書いて合って面白かった
  • Chris Hamons氏に、日本語訳してQiitaのっけていいか聞いたら、元記事にリンクすればOKとのことだったので訳してみることにした
  • 元記事はこちら

スクリーンショット 2017-07-29 13.36.00.png

※あくまでコンパイルの仕組みが理解したいのであって翻訳めっちゃしたいわけではない

本編

ご存知の通り、デフォルトで.NETアプリケーションはビルド時にマシンコードにコンパイルされません。ILと呼ばれる中間レイヤーにコンパイルされ、次のようになります。



[donblas:~/tmp $ cat foo.cs
namespace foo
{
    public static class EntryPoint
    {
        public static void Main()
        {
            System.Console.WriteLine("Hello World");
        }
    }
}
[donblas:~/tmp $ mcs doo.cs -out:foo.exe
[donblas:~/tmp $ monodis foo.exe
.assembly extern mscorlib
{
    .ver 4:0:0:0
    .publickeytoken = (B7 7A 5C 56 19 34 E0 89) // .z\V.4..
}
.assembly 'foo'
{
    .custom instance void class [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute:: '.ctor'() = (
                  01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78    // ....TWrapNonEx
                  63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01      )   // ceptionThrows.
    .hash algorithm 0x00008004
    .ver 0:0:0:0
}
.module foo.exe // GUID = {9A95245A-3B8D-4B98-BC87-E67F67506DC8}

.namespace foo
{
    .class public auto ansi abstract sealed beforefieldinit EntryPoint extends [mscorlib]System.Object
    {
        // method line 1
        .method public static hidebysig default void Main() cil managed
        {
            // Method begins at RVA 0x2050
            .entrypoint
            // Code size 11 (0xb)
            .maxstack 8
            IL_0000:    ldstr "Hello World"
            IL_0005:    call void class [mscorlib]System.Console::WriteLine(string)
            IL_000a:    ret
        } // end of method EntryPoint::Main
    } //end of class foo.EntryPoint
}

アプリケーションの起動時に、monoランタイムは実行時(JIT: Just In Time)にマシンコードにコンパイルします。多くのユースケースでは、素晴らしく魔法のように裏で機能します。

しかし、マシンコードを発行できないといけませんし(これはiOS上ではできません)、かなりの時間を要します。そのため、Xamarin.iOSではビルドの一部として全ての必要なマシンコードをコンパイルするために、デバイス向けのビルドに「完全な」事前(AOT: Ahead Of Time)コンパイルを使用します。

 先週、Xamarin.Macのオプションとして(AOT)コンパイルを追加するために、既存のmonoのインフラストラクチャに基づいたパッチを試しに作ってみました(訳注: もうmergeされてます)。そのパッチでは必要なマシンコードの大部分をコンパイルする「ハイブリッドな」AOTを使用していますが、ランタイムは必要なトランポリン(訳メモ: こういうの? トランポリン)をコンパイルでき、なおかつXamarin.Macで現在動作しているReflection.Emit(とその他のユースケース)を引き続きサポートします(訳メモ: 合ってるか不安)。

この「ハイブリッド」AOTはビルド時間を激増させますが、テストでは平均20%アプリケーション起動時間が改善しました。

パッチのサイズは小さいですが、この変更は広範囲に影響があり、リリース前に重要なテストが必要です。私のチームは追加のXamarin.iOSテストを自動化されたテストスイートに移植する作業を行っており、私はQAに手動テストを実行するよう依頼する予定です。それと、コミュニティにもそれをテストし、何か壊れないか見て欲しいです。

それを試すには、

スクリーンショット 2017-07-29 12.19.05.png

https://github.com/xamarin/xamarin-macios/pull/1340

コメント欄編

Q-1

XamarinのAhead-Of-Time(AOT)コンパイラがXamarin.iOSアプリケーションをネイティブARMアセンブリコードに直接コンパイルすること理解しています。(How Xamarin Works

しかし、なぜ単に普通のコンパイラと区別して「AOT」と呼ばれる必要があるのかわかりません。XamarinのAOTコンパイラと従来のコンパイラとの区別はあるのでしょうか?それとも単なるマーケティング用語なのでしょうか?

A-1

(訳注: compilation→complication?)

標準の.NETビルドシステムではC#/F#コードを.csファイルからバイナリー(.dllか.exe)ファイルに変換するときいつも複雑なコンパイルフローを辿ります。コードをパースして、ILに変換します。

「通常の」.NET実行モデルは、これらのバイナリをメモリにロードし、そのILをマシンコードへと「JIT」でコンパイルします。これにはいくつかの利点(ランタイムはマシンのタイプ/ワークロード/などに基づいて異なるコードをJITできます)と欠点(JITには時間がかかる)があります。

AOTでは、セカンダリビルドプロセスとして最後の部分を実行します。ILを特定のマシンコードにコンパイルし、ディスクに書き込みます(Xamarin.Macのケースではdylibとして)。iOSのようにコード生成が制限されているプラ​​ットフォームでは、AOTが必要です。ただし、macOSでは、これはオプションの手順です。ランタイムがAOTイメージを見つけられない場合、ILからただJITします。

そのため、AOTというラベルを付ける理由は、ソースコードをILアセンブリに変換する「最初のフロー」のコンパイルと区別するためです。

注意すべき点は、「ハイブリッド」AOTを有効にすると、monoのJITコードは依然としてロード時にコードを生成するだろうということです。(訳注: 原文では unless you enable “hybrid” AOT になっていますが、単に unnlessif の間違いではというコメントをいただきました。他の部分の説明からしても正しい指摘だと思うので if で訳しています。)いくつかのトランポリンとジェネリックコードは、実行時にいい感じに生成され、処理にはほとんど時間を要しません。

Q-2

同じ効果がある対応がXamarin.Androidにもされると嬉しいです。アプリ起動時に2〜4秒のスプラッシュ画面の準備時間がかかっています。
Androidアプリをもっと速く読み込めるのであれば、1時間のリリースビルド時間を受け入れます。10程度の必須サポートライブラリの読み込みは最適化されていないようです。何とか事前にマージすることはできませんか?

A-2

ご覧ください。
https://developer.xamarin.com/releases/android/xamarin.android_5/xamarin.android_5.1/#AOT_support

Xamarin.Macのサポートは、Xamarin.Androidの作業にほぼ完全に基づいていました(リリースノートを探すと、たくさんのAOTバグ修正が見つかります)。

感想

  • 純粋に面白かったです。
    • コンパイルのオプションてこんなノリで実装するのか(一般的かどうかは知りません)
    • コミュニティなり記事の読者なりにテスト/報告させてる
  • この記事を書く過程でMonoのAOTに関するページを見つけ、Github管理してました。mono/website/docs/advanced/なるページがあり、自分が知りたいなぁと思ってるのはその辺なので読んだり試してりしていこうと思いました。
  • Xamarin関連のお仕事をするにあたって、 @atsushieno さんのde:codeのこのセッション(Xamarin を支える技術 2017)を聞いたり、この連載(Xamarinを構成するソフトウェア。その主要な10要素とは?)を読んだりしてよく理解はしてないけど何か面白そうというか、ロマンがあるというかで情報を追い始めましたがやっぱ面白そうです(理解はしてない)。