Happy My Life

日常とか技術とか

DalvikVMに替わるランタイム ART

Android 4.4 KitKat 冬コミ原稿リレーの 11月15日分です。

ここではKitKatAndroid 4.4)から追加された新しいAndroidランタイムであるART(Android RunTimeの意味?)の話をします(ランタイムとはソフトウェアを動作させる基盤となるライブラリのこと)これまで利用されていたDalvikVMにかわるランタイムとして2年間かけて開発されてきたようです。

ARTの最大の特徴は、ソフトウェアをネイティブコード(CPUが直接実行できるコード)で実行することを前提に設計されている点です。

ARTを有効化することで、アプリケーションはAOTコンパイラによって事前にネイティブコード化され、実行時にはネイティブコード化されたアプリケーションを実行しています。そのためパフォーマンスが向上が期待できます。

ARTはKitKatで初めて公開されたコードであり開発途中です。このソースコードが公開されたのは、ソースコードが多数の開発者によって改良されるのを期待してのことで、まだ実用段階ではありません。ですのでARTを有効化するのは開発用端末のみに限定し、日常的に利用する端末では従来通りDalvikVMを利用しましょう。

ARTが登場した背景

ARTが登場した背景には、いくつかの理由が考えられます。推測も交えてお話していきます。

まず1つはDalvikVM自体の性能の頭打ちが考えられます。これまでのソフトウェアの実行ファイル(dexファイル)はDalvikVM上で動くバイトコード(中間コード)で構築されていました。この場合はAndroidが動作しているCPU(ARM、x86MIPS)に関係なく1つの実行ファイルが動作するメリットがあります。その反面、実行速度の大部分がDalvikVMに依存します。これまで幾度となくDalvikVMは改良され、一部の動作についてはネイティブとほぼ同等の速度で動作するようになりましたが、全般的にはネイティブコードと同等の速度で動作するところまでは到達していません。

もう1つはOracle社との訴訟問題です。2010年に米Oracle社から「AndroidJava知的所有権を侵害している」という訴えがあったのは記憶に新しい話です。その結果、Google社が不利益を被る事はありませんでしたが、Oracle社は再審の申し立てを計画するなど火種は残ったままです。

DalvikVMを利用しつづけることは、今後も技術的、政治的な問題が残ったままとなります。それらを解決するためにも新しいランタイムを開発する必要があったと推測されます。

DalvikVMとARTを切り替える

Kitkatでは、開発者モードにすることでARTが利用できるようになっています。では、DalvikVMからARTを利用するように切り替えてみます。 ここでは、AOSP版android-4.4.0_r1.0をNexus 7(2012)にインストールしてみました。

DalvikVMとARTを切り替えるには開発者向けオプションで設定します。開発者向けオプションはデフォルトでは非表示ですので、Andorid4.3(JellyBean)と同様に、設定から「タブレット情報」 > 「ビルド番号」を5回タップして表示させます。

開発者向けオプションで「ランタイムを選択」を選択し、「ARTを使用」を選択して再起動します。以下の写真では、ソースコードからビルドしたKitkatをインストールしたため、ARTデバッグビルドも選択できるようになっています。Nexus5などでは、DalvikVMとARTの二択となっていることでしょう)

art1

art2


再起動後は、「Androidをアップブレードしています」の表示とともに、変換中のファイル数が表示されます。この段階では、DalvikVMの中間コードが含まれたファイル(jarファイル、dexファイル)をART対応にしたネイティブコードに変換しています。変換終了後、通常のホーム画面が表示されます。これですべてのソフトウェアがART上で動作しています。

art31

通常の利用方法では、明確に高速になったなどARTの恩恵を体感することは無いでしょう。ただ、logcatや、ベンチマークでは、その違いが見えてきます。以下にNexus5でのベンチマークスクリーンショットを提供します。

ARTは現在開発中の段階ですが、見ての通りDalvikVMを利用した場合より高速に動作していることが分かります。左がDalvikVM、右はARTで動作させた結果です(Thanks @yakmama)

benchmark-Dalvik

benchmark-ART


アプリケーション開発者への影響は無い

現状は、アプリケーション開発者がARTに対応するため何かする、といった必要はありません。これは、ART対応への処理が、すべてAndroid端末側で行われているためです。またARTの影響が及ぶのはdexファイルのみであり、アイコンなどのリソースについては今までと同じです。変更はありません。

また、将来的にもARTに何か新しい機能が追加されたとしても、Android SDKやADTなどで吸収できる範囲と推測しています。

アプリケーション開発者に影響するとしたら、JavaC/C++以外の開発言語でAndroidアプリケーションが開発できる可能性が出てきた、というところでしょうか :-)

ARTでの実行ファイルの詳細

ここからは、ARTのもうちょっと突っ込んだ話をしてきます(android-4.4.0_r1.0時点での話)

まず、これまでのART対応の実行ファイルは拡張子がoatとなります。このoatファイルは、内部にelf形式の共有ライブラリ(つまり.soファイルと同等)ファイルを含んだコードとなっています。

現時点では、oatファイルはインストールされているdexファイル、jarファイルを元にAndroid端末側で生成していますが、 将来的にはoatファイルを開発用PCで作成できるようにも設計されています。

また、ラインタイム自体の機能も大きく変化しています。これは、DalvikVMのバイトコードを解析、コンパイルする必要がなくなったため、その分シンプルになっています。

動作する仕組みについて

では、ARTを利用した場合、ソフトウェアがどのように動作しているのかみていきます。

DalvikVMからARTに切り替えた際、Android端末側でDalvikVM向け実行ファイル(dexファイル、jarファイル)からoatファイルをdex2oatコマンドを呼びだして変換しています。このとき、logcatでは、以下のようなログが表示されています。

I/dex2oat ( 2943): dex2oat: /data/dalvik-cache/data@app@com.example.artsample-1.apk@classes.dex

このときのプロセスは以下の通りです。

  1. /data/dalvik-cacheディレクトリにあるdexファイルのファイル形式チェック
  2. dex2oatコマンドで、dexファイルやjarファイルをoatファイルへ変換し、/data/dalvik-cacheディレクトリに格納

JellyBeanまでは、/data/dalvik-cacheディレクトリは、dexファイルを最適化したファイル(odexファイル)を格納するためのディレクトリでした。そして、KitKatでは、DalvikVMが起動中のときはodexファイル、ARTが起動中のときはoatファイルを格納するディレクトリとなりました。このoatファイルを生成する処理が、先の「Androidをアップブレードしています」の表示の裏側で行われている処理そのものです。

また、新規にアプリケーションがインストールされた場合は、パッケージマネージャがインストールを検知し、そのタイミングでdex2oatコマンドを使ってoatファイルを生成しています。

では、実際に/data/dalvik-cacheディレクトリに格納されているファイルがどのように変化したのか見てみましょう。ここでは、標準の電卓アプリ(system.app.Calculator.apk)を例に比較します。

電卓アプリの実行ファイルは、/data/dalvik-cacheディレクトリに格納される際にsystem@app@Calculator.apk@classes.dexファイルとして格納されます。これはDalvikVM、ARTのどちらが動作している場合も同じファイル名です。

そしてファイルサイズはDalvikVM、ARTで明らかに異なり、dexファイルでは366272バイト、oatファイルでは795056バイトとなりました。oatファイルの方が倍近くファイルサイズが大きくなっていますが、今後oatファイル内のネイティブコードの最適化が進むと、このファイルサイズの差も縮まることが推測されます。

次にファイルのヘッダ情報を見ていきます。ファイルヘッダを参照することで拡張子に関係なく、DalvikVMで動作している場合はdexファイル、ARTでの実行時はoatファイルであることがわかります。

ARTで動作している場合

$ od -tx1z system@app@Calculator.apk@classes.dex| head -2
0000000 7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00  >.ELF…………<
0000020 03 00 28 00 01 00 00 00 00 00 00 00 34 00 00 00  >..(………4…<

DalvikVMで動作している場合

$ od -tx1z system@app@Calculator.apk@classes.dex| head -2
0000000 64 65 79 0a 30 33 36 00 28 00 00 00 9c ec 04 00  >dey.036.(…….<
0000020 c8 ec 04 00 26 05 00 00 f0 f1 04 00 d0 a4 00 00  >….&………..<

oatファイルはヘッダではelfファイルと表示されています。実際にfileコマンドで確認するとelfファイルとして認識できます。

$ file system@app@Calculator.apk@classes.dex
system@app@Calculator.apk@classes.dex: ELF 32-bit LSB shared object, ARM, version 1 (GNU/Linux), dynamically linked, stripped

oatファイルを解析する

oatファイルは、elfファイルと認識されますが、elfファイルそのものではないのでobjdumpコマンドで逆アセンブルすることはできません。その代わりにKitkatから追加されたoatdumpコマンドがありますので、それを使って解析できます。

$ oatdump –oat-file=data@app@com.example.artsample-1.apk@classes.dex > /tmp/artsample-dis.txt

ここで出力されるコードは全部で16メガバイト程出力されます。ここでは、oatファイルのヘッダ部分や、内部コードの逆アセンブルのコードなどが出力されていますので、興味ある人は解析してみるとよいでしょう。

dex2oatコマンド

dexファイルからoatファイルを生成する際には、このdex2oatコマンド(/system/bin/dex2oct)をartランタイムで呼びだしています。dex2oatコマンドの正体はLLVMを使ったコンパイラドライバです。このdex2oatコマンドは、dex形式をフロントエンドとして取り込み、ARM、x86MIPSなどのネイティブコードを出力しています。出力されるコードは、clangを出力されたネイティブコードとは異なり、ARTに対応したコードとなっています(ガベージコレクション対応のコード等が含まれている)

dex2oatコマンドは、様々なオプションを指定してoatファイルを出力しています(これらオプションのいくつかは組み合わせて指定する必要があります)

dex2oatの主要なオプション

オプション 概要
–dex-file=<dex-file> コンパイルするdexファイルを指定する
–zip-fd=<file-descriptor> classes.dexファイルを含むZIPファイル(APKファイル)のファイルディスクリプタを指定する
–zip-location=<zip-location> –zip-fdのファイルディスクリプタで指定したZIPファイルのフルパス名
–oat-file=<file.oat> 出力するoatファイルを指定する
–oat-fd= 出力するoatファイルのファイルディスクリプタを指定する
–oat-location=<oat-name> –oat-fdのファイルディスクリプタで指定したoatファイルのフルパス名
–bitcode=<file.bc> 任意のビットコードを指定する
–instruction-set=(arm mips|x86) | 出力するインストラクションセットを指定する
–compiler-backend=(Quick QuickGBC|Portable) | バックエンドのコンパイラを指定する
–runtime-arg 初期ヒープサイズなどランタイム時に指定するパラメータを渡す

DalvikVMからARTに切り替えた際に実行されるdex2oatコマンドに渡されるオプションは次の通りです。

I/dex2oat (  562): dex2oat: option[0]=–runtime-arg
I/dex2oat (  562): dex2oat: option[1]=-Xms64m
I/dex2oat (  562): dex2oat: option[2]=–runtime-arg
I/dex2oat (  562): dex2oat: option[3]=-Xmx64m
I/dex2oat (  562): dex2oat: option[4]=–runtime-arg
I/dex2oat (  562): dex2oat: option[5]=-classpath
I/dex2oat (  562): dex2oat: option[6]=–runtime-arg
I/dex2oat (  562): dex2oat: option[7]=
I/dex2oat (  562): dex2oat: option[8]=–runtime-arg
I/dex2oat (  562): dex2oat: option[9]=-compiler-filter:speed
I/dex2oat (  562): dex2oat: option[10]=–boot-image=/data/dalvik-cache/system@framework@boot.art@classes.dex
I/dex2oat (  562): dex2oat: option[11]=–dex-file=/system/priv-app/SettingsProvider.apk
I/dex2oat (  562): dex2oat: option[12]=–oat-fd=56
I/dex2oat (  562): dex2oat: option[13]=–oat-location=/data/dalvik-cache/system@priv-app@SettingsProvider.apk@classes.dex
I/dex2oat (  562): dex2oat: /data/dalvik-cache/system@priv-app@SettingsProvider.apk@classes.dex

新規にアプリケーションをインストールした際に実行されるdex2oatコマンドに渡されるオプションは次の通りです。

I/dex2oat ( 1551): dex2oat: option[0]=–zip-fd=8
I/dex2oat ( 1551): dex2oat: option[1]=–zip-location=/data/app/com.example.artsample-1.apk
I/dex2oat ( 1551): dex2oat: option[2]=–oat-fd=11
I/dex2oat ( 1551): dex2oat: option[3]=–oat-location=/data/dalvik-cache/data@app@com.example.artsample-1.apk@classes.dex
I/dex2oat ( 1551): dex2oat: /data/dalvik-cache/data@app@com.example.artsample-1.apk@classes.dex

dex2oatコマンドは、開発PCで実行するためのdex2oatコマンドも生成されているので、試しにそのdex2oatを呼びだしてみましたが、現時点ではAndroid端末内で呼び出すことが前提となっているコマンドのようで、開発PCから呼びだす際は様々な設定が必要でした。

libartの役割

libartの役割は、どのような役割なのでしょうか。

artファイルはネイティブコードとなったため、DalvikVMランタイムに比べると中間コードの解析やネイティブコードへのトランスレーションが必要なくなった分だけシンプルになっています。しかし、dexファイルというJavaベースのコードから変換しやすくするためでしょうが、いくつか機能は残されています。例えば、ガベージコレクション(mark and sweepが採用されています)、JNIなどがそうです。

有効活用されていない機能

ソースコードを読んでいくといくつか利用されていない、将来的に利用されそうな機能がいくつかあります。将来的には削除されるかもしれませんが、可能性があるということで意味でここで紹介していきます。

まずは、SEA IRです。これは新規に開発されたIR(中間言語)のようです。このIRを使ったメリットが不明ですが、実装されていることは分かりました。

次にPortable Compilerです。これは、どうやら、bitcodeを出力するためのコンパイラのようです。現時点ではdexファイルからoatファイルを生成していますが、将来的にはbcファイル(LLVMのビットコード)ファイルからoatファイルを生成するのではないかと予測しています。

ということで

ARTはKitKatで初登場した技術で、このあとコミュニティのチカラを借りて、よりよい物になっていくことでしょう。これからもARTの動向には注目しておくと何かいいことがあるかもしれません。

次は、@mochicon さんです。