史上最速のandroid

37
史史史史史 Android DroidKaigi2016 史史史史史史史史 kmt-t

Upload: takuya-matsunaga

Post on 12-Apr-2017

1.169 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: 史上最速のAndroid

史上最速の AndroidDroidKaigi2016

僻地のプログラマ kmt-t

Page 2: 史上最速のAndroid

2

自己紹介 ハンドルネーム

@kmt_t 職業

業務系プログラマ 元組み込み系

専門分野 画像処理、ファイルシステム、仮想マシン 自然言語処理、ディープラーニングは最近下火

Page 3: 史上最速のAndroid

3

定例連絡 ワールドワイドでオンリーワンです ART も内容的に半分ぐらい同じです

「 Android の仮想マシン Dalvik 編」発売中!

Page 4: 史上最速のAndroid

ART 本執筆進捗 ART の変更が頻繁なため手が出せず 最近気力充実してるのでそろそろ本気出す ちなみに本執筆終わると精神崩壊レベルで燃え尽きます

「 Android の仮想マシン ART 編」は必ず出します!

Page 5: 史上最速のAndroid

今日話すこと 前回の DroidKaigi は難しかったようなので反省 今回はアプリケーションに近いところで話します 仮想マシン依存の最適化の話です

Page 6: 史上最速のAndroid

今日話すこと 前回の DroidKaigi は難しかったようなので反省 今回はアプリケーションに近いところで話します 仮想マシン依存の最適化の話です注意!ART のコードが頻繁に変わるので将来的に正しい保証はありません

Page 7: 史上最速のAndroid

今日のネタ一覧1. 誰も知らないループのオーバヘッド2. 遅い命令と速い命令3. JNI の秘密

Page 8: 史上最速のAndroid

本日の計測環境

• https://www.genymotion.com/• x86/Android エミュレータ• Android 6.0

GenyMotion

• Intel core i5 6400T (Skylake)• メモリ 32GB

PC 環境

Page 9: 史上最速のAndroid

解析ツール• DEX ファイル解析• バイトコードレベルの解析• 今回は使いません

dexdump

• OAT ファイル解析• コンパイルされたバイトコード、ネイティブコードの解析• 今回はこっちを使います

oatdump

Page 10: 史上最速のAndroid

ネタその 1誰も知らないループのオーバヘッドこんな簡単なことに誰も気が付かない

Page 11: 史上最速のAndroid

何の変哲もないループint N = 100000000;int ret = 0;for (int i = 0; i < N; ++i) { ret += i;}

Page 12: 史上最速のAndroid

ベンチマーク

Page 13: 史上最速のAndroid

何となくアンロールしてみる アンロールとは複数回のループをベタで展開することint ret = 0;for (int i = 0; i < N; i += 8) { ret += i; ret += i; ret += i; ret += i; ret += i; ret += i; ret += i; ret += i;}

Page 14: 史上最速のAndroid

ベンチマーク アンロール前 (518ms) アンロール後 (331ms)

Page 15: 史上最速のAndroid

あれ、結構効果ある 単純なアンロールが結構効きますね

Page 16: 史上最速のAndroid

バイトコードを見てみる 1: int net.remoteplace.myapplication.Test.test1() DEX CODE: 0x0000: 1400 00e1 f505 | const v0, #+100000000 0x0003: 1202 | const/4 v2, #+0 0x0004: 1201 | const/4 v1, #+0 0x0005: 3501 0600 | if-ge v1, v0, +6 0x0007: b012 | add-int/2addr v2, v1 0x0008: d801 0101 | add-int/lit8 v1, v1, #+1 0x000a: 28fb | goto -5 0x000b: 0f02 | return v2

Page 17: 史上最速のAndroid

ネイティブコードを見てみる ( 長いので割愛 ) ( ループの度に GC チェックを実行 )

Page 18: 史上最速のAndroid

こいつを生成してるところ ループの度に謎の処理が入る

art/compiler/dex/quick/mir_to_lir.cc

case Instruction::IF_EQ:case Instruction::IF_NE:case Instruction::IF_LT:case Instruction::IF_GE:case Instruction::IF_GT:case Instruction::IF_LE: { if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) { GenSuspendTest(opt_flags); } LIR* taken = &label_list[bb->taken]; GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken); break; }

Page 19: 史上最速のAndroid

こいつの正体 後ろ向き ( 繰り返し ) のジャンプで生成される 正体は GC チェックの処理 GC チェックは GC ポイントで実行される GC ポイントでは GC マップというガイド情報が用意される GC マップはコンパイル時に生成されるため、効率が良い

Page 20: 史上最速のAndroid

この例の教訓 ART のループは GC チェックが実行される きわめて小さいループではアンロールが効く GC が呼ばれるのでループでオブジェクトの生成を避ける

Page 21: 史上最速のAndroid

ネタその 2遅い命令と速い命令バイトコードを読めると三文の得

Page 22: 史上最速のAndroid

遅い命令と速い命令 バイトコードには似た処理を行う遅い命令と速い命令がある バイトコード命令ごとの処理時間を見積もるには? バイトコード命令ごとの処理時間を合算すればよいインタープリタのソースを読むと実行速度のイメージが沸きます!

Page 23: 史上最速のAndroid

ART のアーキテクチャ復習 仮想マシンレジスタに値を入れて計算 ローカル変数は仮想マシンレジスタに保存される

Page 24: 史上最速のAndroid

MOVE と MOVE_LONG 仮想マシンレジスタの値をコピーするバイトコード命令 MOVE は 32ビット MOVE_LONG は 64ビット ART の仮想マシンレジスタは幅は 32ビット 64ビットリードは仮想マシンレジスタを 2 回リードする x86 でも 2 回リードする double型も同様 そのため浮動小数点の計算は float のほうが速い

Page 25: 史上最速のAndroid

IF_EQ と IF_EQZ とそのバリエーション IF_EQ はふたつの仮想マシンレジスタの値が一致すると分岐 IF_EQZ は仮想マシンレジスタの値と 0 が一致すると分岐 IF_EQ は x86 の cmp 命令に変換 IF_EQZ は x86 の test 命令に変換 IF_EQZ の方が使用する CPU レジスタが少ないので速くなる

Page 26: 史上最速のAndroid

いつかベンチマーク取って公開します 今回は全バイトコード命令のベンチマークを取る予定でした 資料作成の時間の関係で作れませんでした いつか実施して結果を公開します…

Page 27: 史上最速のAndroid

ネタその 3JNI の秘密

OSS に情報漏洩はない

Page 28: 史上最速のAndroid

JNI の秘密 Dalvik では組み込みメソッドがあり、一部のフレームワークのメソッドは仮想マシン内にネイティブで実装されていた 組み込みメソッドは JNI と違い呼び出しが速い ART では組み込み関数は廃止され、 JNI に置き換わった JNI が速くなったのもあるが、実は隠し機能がある 隠し機能を使うことで JNI 呼び出しが高速化される

Page 29: 史上最速のAndroid

高速 JNI関数一覧 (1/2)クラス名 メソッド名

java/lang/Class

classForName getDeclaredMethodInternalgetDeclaredConstructorInternal getDeclaredMethodsUncheckedgetDeclaredConstructorsInternal getNameNativegetDeclaredFieldInternal getProxyInterfacesgetDeclaredFields getPublicDeclaredFieldsgetDeclaredFieldsUnchecked newInstance

java/lang/Object internalClone notifyAllnotify wait

java/lang/reflect/Array createMultiArray createObjectArrayjava/lang/reflect/Constructor newInstance  

java/lang/reflect/Field

get setgetBoolean setBooleangetByte setBytegetChar setChargetDouble setDoublegetFloat setFloatgetInt setIntgetLong setLonggetShort setShort

java/lang/reflect/Method invoke getExceptionTypesNative

Page 30: 史上最速のAndroid

高速 JNI関数一覧 (2/2)クラス名 メソッド名

java/lang/StringFactory newStringFromBytes newStringFromStringnewStringFromChars  

java/lang/System

arraycopy arraycopyLongUncheckedarraycopyCharUnchecked arraycopyFloatUncheckedarraycopyByteUnchecked arraycopyDoubleUncheckedarraycopyShortUnchecked arraycopyBooleanUncheckedarraycopyIntUnchecked identityHashCode

java/lang/Throwable nativeFillInStackTrace nativeGetStackTrace

libcore/util/CharsetUtilsasciiBytesToChars toIsoLatin1BytesisoLatin1BytesToChars toUtf8BytestoAsciiBytes  

sun/misc/Unsafe

compareAndSwapInt putIntcompareAndSwapLong putOrderedIntcompareAndSwapObject getLonggetIntVolatile putLongputIntVolatile putOrderedLonggetLongVolatile getObjectputLongVolatile putObjectgetObjectVolatile putOrderedObjectputObjectVolatile getArrayBaseOffsetForComponentTy

pegetInt getArrayIndexScaleForComponentTy

pe

Page 31: 史上最速のAndroid

高速 JNI関数の使い方 実は普通のアプリケーションでも使用できる 型ディスクリプタの頭に「 ! 」付けるだけ

JNINativeMethod m = {“hoge", “!()Z", hoge};  env->RegisterNatives(clazz, &m, 1); 

Page 32: 史上最速のAndroid

高速 JNI の注意事項 高速 JNI は仮想マシンのステート切り替えのオーバーヘッドがない 高速 JNI 内部では Java オブジェクトにアクセスできない 高速 JNI で長い時間がかかる処理を行うと GC に悪影響がある この機能は使っていいか俺は知りません 当然 Dalvik では動きません

Page 33: 史上最速のAndroid

まとめ

Page 34: 史上最速のAndroid

最速を目指すための作法 for 文の中でオブジェクトを生成しない 数値計算の場合はループをアンロールする 遅い命令は避ける 高速 JNI を使う

Page 35: 史上最速のAndroid

最速を目指すための作法 for 文の中でオブジェクトを生成しない 数値計算の場合はループをアンロールする ローカル変数のオブジェクトにアクセスしない フィールド変数はローカル変数にコピーする 遅い命令は避ける 高速 JNI を使う

注意!最速を目指す必要などない!

Page 36: 史上最速のAndroid

Android Guru を目指して バイトコードと逆アセンブラは読みましょう インタープリタのコードは読みましょう ベンチマークは重要です

Page 37: 史上最速のAndroid

ご清聴ありとうございました

質問はありますか!?