2009年09月02日
AndroidのDalvikVMのインタープリタをFPU命令で少し高速化する(その2)
前回はDalvik VMのインタープリタのfloatの四則演算だけFPU命令を使うように修正してみました。今回はdoubleの四則演算でもFPU命令を使うように修正します。今回の修正で私が即席で作った速度測定プログラムをエミュレータで実行したところ、約260msecかかっていたものが約85msecにまで短縮できました。これは結構効果がありそうです。
2009年11月のEclairのソースリリースでだいぶ状況が変わっています。こちらも参照してください。
まず、floatのときと同じ変更をしてみたが...失敗
floatのときには一命令のライブラリ呼び出しを合計4命令のFPU命令に置き換えました。単純ですが、これだと差し引き3命令分サイズが増えてしまいます。アセンブラのインタープリタでは高速化のためにひとつのDXコードに対する処理を64バイトで書かなければならないようになっています。ARMモードだと16命令分です。
floatのときには命令数に余裕があったのですが、doubleでは3命令増えると合計が17命令になってはみだしてしまいました。
変更する範囲を広げる
命令数を増やさないようにしつつFPU命令を使うためには、ライブラリ呼び出しだけを置き換えるのでなく、メモリの読み書きの部分も変更して、メモリから直接FPUレジスタに読み書きするようにします。
以下のようなCのプログラムからメモリからFPUレジスタへの読み書きの方法を調べます。
void dadd(double *x, double *y) { *x += *y; } void dsub(double *x, double *y) { *x -= *y; } void dmul(double *x, double *y) { *x *= *y; } void ddiv(double *x, double *y) { *x /= *y; } void fadd(float *x, float *y) { *x += *y; } void fsub(float *x, float *y) { *x -= *y; } void fmul(float *x, float *y) { *x *= *y; } void fdiv(float *x, float *y) { *x /= *y; }
FPUを使わない場合
dmul: @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 ldmia r1, {r2-r3} stmfd sp!, {r4, lr} mov r4, r0 ldmia r0, {r0-r1} bl __aeabi_dmul stmia r4, {r0-r1} ldmfd sp!, {r4, pc}
FPUを使う場合
dmul: @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 @ link register save eliminated. fldd d5, [r1, #0] fldd d7, [r0, #0] @ lr needed for prologue fmuld d6, d7, d5 fstd d6, [r0, #0] bx lr
これを参考にして、試しにインタープリタの一ヶ所だけを以下のように変更してみます。
diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S index fa2ad9a..eb6f8a8 100644 --- a/vm/mterp/out/InterpAsm-armv5te.S +++ b/vm/mterp/out/InterpAsm-armv5te.S @@ -5267,8 +5267,10 @@ d2i_doconv: add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] - ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 - ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 +@ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 +@ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 + fldd d6, [r2, #0] + fldd d7, [r3, #0] .if 0 orrs ip, r2, r3 @ second arg (r2-r3) is zero? beq common_errDivideByZero @@ -5276,9 +5278,13 @@ d2i_doconv: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST @ optional op; may set condition codes - bl __aeabi_dmul @ result<- op, r0-r3 changed +@ bl __aeabi_dmul @ result<- op, r0-r3 changed + fmuld d6, d7, d6 + GET_INST_OPCODE(ip) @ extract opcode from rINST - stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 +@ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 + fstd d6, [r9, #0] + GOTO_OPCODE(ip) @ jump to next instruction /* 14-17 instructions */
この変更を入れると以下のようになります。
InterpAsm-armv5te.S
/* ------------------------------ */ .balign 64 .L_OP_MUL_DOUBLE: /* 0xad */ /* File: armv5te/OP_MUL_DOUBLE.S */ /* File: armv5te/binopWide.S */ /* * Generic 64-bit binary operation. Provide an "instr" line that * specifies an instruction that performs "result = r0-r1 op r2-r3". * This could be an ARM instruction or a function call. (If the result * comes back in a register other than r0, you can override "result".) * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (r1). Useful for integer division and modulus. * * for: add-long, sub-long, div-long, rem-long, and-long, or-long, * xor-long, add-double, sub-double, mul-double, div-double, * rem-double * * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. */ /* binop vAA, vBB, vCC */ FETCH(r0, 1) @ r0<- CCBB mov r9, rINST, lsr #8 @ r9<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] @ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 @ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 fldd d6, [r2, #0] fldd d7, [r3, #0] .if 0 orrs ip, r2, r3 @ second arg (r2-r3) is zero? beq common_errDivideByZero .endif FETCH_ADVANCE_INST(2) @ advance rPC, load rINST @ optional op; may set condition codes @ bl __aeabi_dmul @ result<- op, r0-r3 changed fmuld d6, d7, d6 GET_INST_OPCODE(ip) @ extract opcode from rINST @ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 fstd d6, [r9, #0] GOTO_OPCODE(ip) @ jump to next instruction /* 14-17 instructions */ /* ------------------------------ */ .balign 64
バスエラー発生。なんで?!
このように一ヶ所だけ変更したインタープリタでエミュレータを起動するとエラーが発生して起動に失敗してしまいました。
logcatによると zygoteのプロセスでバスエラーが発生したようです。
I/DEBUG ( 543): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** I/DEBUG ( 543): Build fingerprint: 'generic/generic/generic/:Donut/Donut/eng.koba.20090827.140821:eng/test-keys' I/DEBUG ( 543): pid: 590, tid: 590 >>> zygote <<< I/DEBUG ( 543): signal 7 (SIGBUS), fault addr 00000000 I/DEBUG ( 543): r0 00001412 r1 4104bbe4 r2 4104bbdc r3 4104bbe4 I/DEBUG ( 543): r4 41748086 r5 4104bb94 r6 bef614e8 r7 ad00e640 I/DEBUG ( 543): r8 000012ad r9 4104bbdc 10 4104bb84 fp 00000000 I/DEBUG ( 543): ip 000000ad sp bef614a8 lr ad01071c pc ad01119c cpsr 20000010 I/DEBUG ( 543): #00 pc 0001119c /system/lib/libdvm.so I/DEBUG ( 543): #01 pc 00017d90 /system/lib/libdvm.so I/DEBUG ( 543): #02 pc 000177d4 /system/lib/libdvm.so I/DEBUG ( 543): #03 pc 00052974 /system/lib/libdvm.so I/DEBUG ( 543): #04 pc 00052992 /system/lib/libdvm.so
バスエラーを起こしたpcは0xad01119c
Androidではシステムの共有ライブラリは固定番地にprelinkされます。
以下のファイルを調べると0xad01119cはlibdvm.so の中だとわかります。
$TOP/build/core/prelink-linux-arm.map
# 0xC0000000 - 0xFFFFFFFF Kernel # 0xB0100000 - 0xBFFFFFFF Thread 0 Stack # 0xB0000000 - 0xB00FFFFF Linker # 0xA0000000 - 0xBFFFFFFF Prelinked System Libraries # 0x90000000 - 0x9FFFFFFF Prelinked App Libraries # 0x80000000 - 0x8FFFFFFF Non-prelinked Libraries # 0x40000000 - 0x7FFFFFFF mmap'd stuff # 0x10000000 - 0x3FFFFFFF Thread Stacks # 0x00000000 - 0x0FFFFFFF .text / .data / heap ... # core dalvik runtime support libandroid_servers.so 0xAD900000 #libicudata.so 0xAD600000 libicuuc.so 0xAD500000 libicui18n.so 0xAD400000 libandroid_runtime.so 0xAD300000 libnativehelper.so 0xAD200000 libdvm-ARM.so 0xAD100000 libdvm.so 0xAD000000
libdvm.soを逆アセンブルして先頭から 0x1119cのところを見てみると
$ arm-eabi-objdump -d out/target/product/generic/symbols/system/lib/libdvm.so |less ... 11178: e1a00000 nop (mov r0,r0) 1117c: e1a00000 nop (mov r0,r0) 11180: e1d400b2 ldrh r0, [r4, #2] 11184: e1a09428 mov r9, r8, lsr #8 11188: e20020ff and r2, r0, #255 ; 0xff 1118c: e1a03420 mov r3, r0, lsr #8 11190: e0859109 add r9, r5, r9, lsl #2 11194: e0852102 add r2, r5, r2, lsl #2 11198: e0853103 add r3, r5, r3, lsl #2 >> 1119c: ed926b00 fldd d6, [r2] 111a0: ed937b00 fldd d7, [r3] 111a4: e1f480b4 ldrh r8, [r4, #4]! 111a8: ee276b06 fmuld d6, d7, d6 111ac: e208c0ff and ip, r8, #255 ; 0xff 111b0: ed896b00 fstd d6, [r9] 111b4: e087f30c add pc, r7, ip, lsl #6 111b8: e1a00000 nop (mov r0,r0) 111bc: e1a00000 nop (mov r0,r0) 111c0: e1d400b2 ldrh r0, [r4, #2] ...
1119cのfldd d6, [r2]はまさしく今追加した部分です。
でもなぜバスエラーになるのでしょう?
r2の値は0x4104bbdcです。8バイトのデータの読み出しが8バイト境界になっていないのがまずいのでしょうか?
そんなはずはないと思って、ARMのドキュメントを調べ、またARM1136の実ボードでコードの断片を動かしてみてこのflddが4バイト境界のアドレスで問題なく動作することを確認しました。
このデバッグのダンプしているところのバグかもしれないと思って、その部分を追ってみたり。
最後にはqemuを疑うところまでいきましたが、どうやらそれが当たりだったようです。
qemuを変更する
64bitのデータの読み書きでアライメントのチェックをしている箇所をqemuのソースで探しました。マクロが多用された読みにくいソースでしたが該当箇所をなんとか見つけることができました。
試行錯誤で修正してなんとか動くようになりました。やはりqemuの問題でした。
最終的には最新版のqemuとソースを比較し、以下の1箇所の修正でうまくいくことがわかりました。
cd external/qemu
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 36de55b..41219f6 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -70,7 +70,7 @@ uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr); #define MMUSUFFIX _mmu -#define ALIGNED_ONLY 1 +/*#define ALIGNED_ONLY 1*/ #define SHIFT 0 #include "softmmu_template.h"
まとめ
- androidのソースツリーにあるqemuは64bitのデータの読み書きの時のアライメントチェックが間違っていました。
- 前回と今回の変更でfloatとdoubleの四則演算がFPUで高速化されました。まだ比較や型変換などFPUで高速化できる余地があります。
実機での効果は未測定です。どなたかAndroidをFPUつきの実機に移植した方はぜひ試してみてください。また、アプリでの浮動小数点演算の速度のちょうどいいベンチマークテストがあったら紹介してください。
次回はユーザーランドをまるごとFPUを有効にしてビルドするための変更点を紹介します。
以下に今回のソースの変更点と速度測定に使った即席プログラムを張り付けておきます。
インタープリタのソース変更
前回のfloatの変更に追加して以下を変更します。
diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S index fa2ad9a..bbc9d21 100644 --- a/vm/mterp/out/InterpAsm-armv5te.S +++ b/vm/mterp/out/InterpAsm-armv5te.S @@ -5177,8 +5177,10 @@ d2i_doconv: add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] - ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 - ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 +@ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 +@ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 + fldd d6, [r2, #0] + fldd d7, [r3, #0] .if 0 orrs ip, r2, r3 @ second arg (r2-r3) is zero? beq common_errDivideByZero @@ -5186,9 +5188,13 @@ d2i_doconv: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST @ optional op; may set condition codes - bl __aeabi_dadd @ result<- op, r0-r3 changed +@ bl __aeabi_dadd @ result<- op, r0-r3 changed + faddd d6, d6, d7 + GET_INST_OPCODE(ip) @ extract opcode from rINST - stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 +@ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 + fstd d6, [r9, #0] + GOTO_OPCODE(ip) @ jump to next instruction /* 14-17 instructions */ @@ -5222,8 +5228,10 @@ d2i_doconv: add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] - ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 - ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 +@ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 +@ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 + fldd d6, [r2, #0] + fldd d7, [r3, #0] .if 0 orrs ip, r2, r3 @ second arg (r2-r3) is zero? beq common_errDivideByZero @@ -5231,9 +5239,13 @@ d2i_doconv: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST @ optional op; may set condition codes - bl __aeabi_dsub @ result<- op, r0-r3 changed +@ bl __aeabi_dsub @ result<- op, r0-r3 changed + fsubd d6, d6, d7 + GET_INST_OPCODE(ip) @ extract opcode from rINST - stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 +@ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 + fstd d6, [r9, #0] + GOTO_OPCODE(ip) @ jump to next instruction /* 14-17 instructions */ @@ -5267,8 +5279,10 @@ d2i_doconv: add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] - ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 - ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 +@ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 +@ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 + fldd d6, [r2, #0] + fldd d7, [r3, #0] .if 0 orrs ip, r2, r3 @ second arg (r2-r3) is zero? beq common_errDivideByZero @@ -5276,9 +5290,13 @@ d2i_doconv: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST @ optional op; may set condition codes - bl __aeabi_dmul @ result<- op, r0-r3 changed +@ bl __aeabi_dmul @ result<- op, r0-r3 changed + fmuld d6, d6, d7 + GET_INST_OPCODE(ip) @ extract opcode from rINST - stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 +@ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 + fstd d6, [r9, #0] + GOTO_OPCODE(ip) @ jump to next instruction /* 14-17 instructions */ @@ -5312,8 +5330,10 @@ d2i_doconv: add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] - ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 - ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 +@ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 +@ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 + fldd d6, [r2, #0] + fldd d7, [r3, #0] .if 0 orrs ip, r2, r3 @ second arg (r2-r3) is zero? beq common_errDivideByZero @@ -5321,9 +5341,13 @@ d2i_doconv: FETCH_ADVANCE_INST(2) @ advance rPC, load rINST @ optional op; may set condition codes - bl __aeabi_ddiv @ result<- op, r0-r3 changed +@ bl __aeabi_ddiv @ result<- op, r0-r3 changed + fdivd d6, d6, d7 + GET_INST_OPCODE(ip) @ extract opcode from rINST - stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 +@ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 + fstd d6, [r9, #0] + GOTO_OPCODE(ip) @ jump to next instruction /* 14-17 instructions */ @@ -6467,8 +6491,10 @@ d2i_doconv: and r9, r9, #15 add r1, rFP, r1, lsl #2 @ r1<- &fp[B] add r9, rFP, r9, lsl #2 @ r9<- &fp[A] - ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 - ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 +@ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 +@ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 + fldd d7, [r1, #0] + fldd d6, [r9, #0] .if 0 orrs ip, r2, r3 @ second arg (r2-r3) is zero? beq common_errDivideByZero @@ -6476,9 +6502,13 @@ d2i_doconv: FETCH_ADVANCE_INST(1) @ advance rPC, load rINST @ optional op; may set condition codes - bl __aeabi_dadd @ result<- op, r0-r3 changed +@ bl __aeabi_dadd @ result<- op, r0-r3 changed + faddd d6, d6, d7 + GET_INST_OPCODE(ip) @ extract opcode from rINST - stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 +@ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 + fstd d6, [r9, #0] + GOTO_OPCODE(ip) @ jump to next instruction /* 12-15 instructions */ @@ -6509,8 +6539,10 @@ d2i_doconv: and r9, r9, #15 add r1, rFP, r1, lsl #2 @ r1<- &fp[B] add r9, rFP, r9, lsl #2 @ r9<- &fp[A] - ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 - ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 +@ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 +@ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 + fldd d7, [r1, #0] + fldd d6, [r9, #0] .if 0 orrs ip, r2, r3 @ second arg (r2-r3) is zero? beq common_errDivideByZero @@ -6518,9 +6550,13 @@ d2i_doconv: FETCH_ADVANCE_INST(1) @ advance rPC, load rINST @ optional op; may set condition codes - bl __aeabi_dsub @ result<- op, r0-r3 changed +@ bl __aeabi_dsub @ result<- op, r0-r3 changed + fsubd d6, d6, d7 + GET_INST_OPCODE(ip) @ extract opcode from rINST - stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 +@ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 + fstd d6, [r9, #0] + GOTO_OPCODE(ip) @ jump to next instruction /* 12-15 instructions */ @@ -6551,8 +6587,10 @@ d2i_doconv: and r9, r9, #15 add r1, rFP, r1, lsl #2 @ r1<- &fp[B] add r9, rFP, r9, lsl #2 @ r9<- &fp[A] - ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 - ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 +@ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 +@ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 + fldd d7, [r1, #0] + fldd d6, [r9, #0] .if 0 orrs ip, r2, r3 @ second arg (r2-r3) is zero? beq common_errDivideByZero @@ -6560,9 +6598,13 @@ d2i_doconv: FETCH_ADVANCE_INST(1) @ advance rPC, load rINST @ optional op; may set condition codes - bl __aeabi_dmul @ result<- op, r0-r3 changed +@ bl __aeabi_dmul @ result<- op, r0-r3 changed + fmuld d6, d6, d7 + GET_INST_OPCODE(ip) @ extract opcode from rINST - stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 +@ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 + fstd d6, [r9, #0] + GOTO_OPCODE(ip) @ jump to next instruction /* 12-15 instructions */ @@ -6593,8 +6635,10 @@ d2i_doconv: and r9, r9, #15 add r1, rFP, r1, lsl #2 @ r1<- &fp[B] add r9, rFP, r9, lsl #2 @ r9<- &fp[A] - ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 - ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 +@ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 +@ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 + fldd d7, [r1, #0] + fldd d6, [r9, #0] .if 0 orrs ip, r2, r3 @ second arg (r2-r3) is zero? beq common_errDivideByZero @@ -6602,9 +6646,13 @@ d2i_doconv: FETCH_ADVANCE_INST(1) @ advance rPC, load rINST @ optional op; may set condition codes - bl __aeabi_ddiv @ result<- op, r0-r3 changed +@ bl __aeabi_ddiv @ result<- op, r0-r3 changed + fdivd d6, d6, d7 + GET_INST_OPCODE(ip) @ extract opcode from rINST - stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 +@ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1 + fstd d6, [r9, #0] + GOTO_OPCODE(ip) @ jump to next instruction /* 12-15 instructions */
速度測定に使用したプログラム
package com.example.helloandroid; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class FPUtestDouble extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView tv = new TextView(this); tv.setText("FPUtestDouble: \n" + testTime() + " : " + testResult() + "\n" + testTime() + " : " + testResult() + "\n" + testTime() + " : " + testResult() + "\n" + testTime() + " : " + testResult() + "\n" + testTime() + " : " + testResult() + "\n" + testTime() + " : " + testResult() + "\n" + testTime() + " : " + testResult() + "\n" + testTime() + " : " + testResult() + "\n" + testTime() + " : " + testResult() + "\n" + testTime() + " : " + testResult() + "\n"); setContentView(tv); } double result; String testResult() { return Double.toString(result); } String testTime() { long start, stop; start = System.currentTimeMillis(); result = test(); stop = System.currentTimeMillis(); return Long.toString(stop - start); } static final int COUNT = 100000; double test() { double x, y, z, w; x = y = z = w = 1.0f; for (int i = 0; i < COUNT; i++) { x += 0.0001d; y -= 0.0001d; z *= 1.0001d; w /= 0.9999d; } return x + y + z + w; } }