XamlCの概要
Xamarin.Forms 1.5.1-pre1がアナウンスされ、その中に XamlC と呼ばれる機能が含まれています。
Xamarin.Forms 1.5.1-pre1 Released - Xamarin Forums
以下の効果があります。
- 実行時にXAMLを解析しないことによるロード時間の減少
- アセンブリに.xmlファイルを含まなくなることによるアプリサイズの減少
XamlCの有効化は属性で指定します。
アセンブリ全体で有効化:
[assembly:Xamarin.Forms.Xaml.XamlCompilation (Xamarin.Forms.Xaml.XamlCompilationOptions.Compile)]
クラス単位で有効化:
[Xamarin.Forms.Xaml.XamlCompilation (Xamarin.Forms.Xaml.XamlCompilationOptions.Compile)]
注意
XamlCはβテスト版です、XF 1.5.1正式版までに外される可能性があります。(以前もPreview版に盛り込まれましたが「今はバグフィックスを優先する」と先送りにされました。)
Xamarin.FormsにおけるXAML解釈
PageやViewの新規作成時にXAML付きのテンプレートを選ぶと、.xamlファイルとpartial指定付きの.csファイルが生成されます。そして、
実行時に.csファイルのコンストラクタ内の InitializeComponent()
で.xamlファイルをパースしViewを組み上げていくという仕組みです。
XamlCはコンパイル時にXAMLを解析し、相当するILを生成します。
XamlCompilationOptions
ちなみに XamlCompilationOptions
の定義はこうなっています。
[Flags]
public enum XamlCompilationOptions
{
Skip = 1,
Compile = 2
}
XamlCompilationOptions.Skip
はアセンブリ全体でXamlCを有効にしたうえで、特定のクラスだけプリコンパイルさせたくない時に使います。(多分)
試してみる
(※環境はMac&Xamarin Studioです)
ソリューション作成から「Blank Xamarin.Forms App」で新規作成します。(今回はSharedにしました)
iOS、AndroidのプロジェクトのXamarin.Formsを1.5.1-pre1に更新します。(Preview版nugetパッケージへの更新方法は以前の記事にスクリーンショット付きの手順があります。)
Sharedプロジェクトに新しいContentPage(XAML)を追加します。
App
クラスを修正します。
MainPage = new MyPage();
本当にプリコンパイルされているか?
Label
のスペルをわざと間違えて"Lable"にしてみます。
<ContentPage.Content>
<Lable Text="Hello XamlC!"/>
</ContentPage.Content>
.csファイルにはXamlCompilation属性を指定します。
[Xamarin.Forms.Xaml.XamlCompilation (Xamarin.Forms.Xaml.XamlCompilationOptions.Compile)]
public partial class MyPage : ContentPage
ビルドすると...
おお!コンパイル時点でエラーになりました。ちゃんとXamlCが働いているようです。
おまけ:存在しないプロパティ
Label
の部分を少し修正してみました。
<Label Text="Hello XamlC!" ForeColor="#FF8888" />
正しいプロパティ名はTextColor
で"ForeColor"というプロパティはLabelクラスには存在しません。
結果
エラー無くコンパイルされ、実行時例外も発生しませんでした。これはAttached Property(例 Grid.Row
)のようにコントロールに存在しないプロパティが書かれる場合があるため、エラーにできていないように思えます。
...でもXamlCオフだと実行時例外なんですよね、コレ。
おまけ:間違ったプロパティ値の指定
今度は値の方を間違えてみました。TextColor
をWebカラー式に指定しようとして、先頭の「#」が抜けています。
<Label Text="Hello XamlC!" TextColor="FF8888" />
結果
実行時例外となりました。これは IValueConverter.Convert()
を呼び出すILに変換されたため、実行時にstringからColorへ変換できずに例外発生になったものと思われます。
プリコンパイル時にダイレクトに代入するコードに変換してくれるようになるといいですね。(オーバーヘッドも減るし、間違いにも気付けるので)
速度的な話
肝心の高速化の検証。
.xaml側はこう、
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="XamlC.MyPage">
<ContentPage.Content>
<Label />
</ContentPage.Content>
</ContentPage>
.csは InitializeComponent()
にかかった時間を計測します。
public MyPage()
{
var stopwatch = Stopwatch.StartNew();
InitializeComponent();
stopwatch.Stop();
((Label)Content).Text = stopwatch.ElapsedMilliseconds + "ms";
}
ほいで計測手順と環境
- 端末はiPhone 5(iOS 8.4.1)
- Debugビルドで実機に転送、デバッグ自体はすぐに停止。
- 実機に転送されたアプリを起動。
- Labelに表示された結果を確認後、アプリを終了させる。
- 10回ずつ計測。
結果
- XamlC無効
- 平均: 103.9 ms
- 個別: 105, 103, 101, 104, 102, 105, 106, 110, 102, 101 (ms)
- XamlC有効
- 平均: 19 ms
- 個別: 19, 18, 36, 16, 16, 19, 16, 16, 18, 16 (ms)
InitializeComponent()
にかかる時間がおよそ1/5に短縮されました!
かなり効果があると言えるでしょう。
複雑なXAMLであればより大きな差になるでしょう。しかし、レイアウト自体は実行時に行われるため、座標計算などのオーバーヘッドにはXamlCの効果が及ばない点に注意が必要です。
おまけ:C#で書いた場合
ついでにC#オンリーで書いた場合のタイムも計測してみました。
public CsPage()
{
var stopwatch = Stopwatch.StartNew();
Content = new Label();
stopwatch.Stop();
((Label)Content).Text = stopwatch.ElapsedMilliseconds + "ms";
}
結果
- C#オンリー
- 平均: 15.6 ms
- 個別: 15, 15, 15, 15, 19, 15, 15, 15, 16, 16 (ms)
XamlCを有効にした場合よりも少し速いのはコードの違いか、何かしら最適化が働いているのか...
まとめ
- 高速化はかなり有効、C#で書いた場合に迫る速度になる。
- XAML記述サポートとして期待するにはもう一歩踏み込んだ解析能力が必要。(XamlC本来の目的ではない)
- ぶっちゃけ全部C#で書くと速い。