その2では x = ++i; や x = i++; の様な場面で使った時の速度比較。
ボタンを押すとテストコードを実行する以下の様なプログラムを準備し、iPhone 4Sでテストしました。
@implementation ViewController #define LOOP_NUM 1000000 static int x_array[LOOP_NUM]; - (void)typeA { int i, j; CFAbsoluteTime start; float x; start = CFAbsoluteTimeGetCurrent(); j = 0; for (i=0; i<LOOP_NUM; i++) { x_array[i] = ++j; } NSLog(@"type A:%lf", CFAbsoluteTimeGetCurrent() - start); // コンパイラの最適化対策の念のための処理 for (i=0; i<LOOP_NUM; i++) { x += x_array[i]*0.5; } NSLog(@"dummy %f", x); } - (void)typeB { int i, j; CFAbsoluteTime start; float x; start = CFAbsoluteTimeGetCurrent(); j = 0; for (i=0; i<LOOP_NUM; i++) { x_array[i] = j++; } NSLog(@"type B:%lf", CFAbsoluteTimeGetCurrent() - start); // コンパイラの最適化対策の念のための処理 for (i=0; i<LOOP_NUM; i++) { x += x_array[i]*0.5; } NSLog(@"dummy %f", x); } - (IBAction)test:(id)sender { for (int i=0; i<5; i++) { [self typeA]; [self typeB]; } }
プログラムはインクリメントを変数の前に記述する「type A」、インクリメントの後に記述する「type B」の2つを準備し、ボタンが押されたタイミングで以下のコードで5回づつ速度テストを行いました。
for (int i=0; i<5; i++) { [self typeA]; [self typeB]; }
処理速度の結果はこちら。
回 | type A | type B |
---|---|---|
1 | 0.024117 | 0.019300 |
2 | 0.018235 | 0.018811 |
3 | 0.018916 | 0.018695 |
4 | 0.018851 | 0.018934 |
5 | 0.018910 | 0.019431 |
呼び出す順番がベンチマークに影響にする可能性も考え、以下の様に逆の順番でも確認しました。
for (int i=0; i<5; i++) { [self typeB]; [self typeA]; }
そちらの結果はこちら。
回 | type B | type A |
---|---|---|
1 | 0.024352 | 0.018308 |
2 | 0.018748 | 0.018356 |
3 | 0.019218 | 0.018330 |
4 | 0.019438 | 0.018436 |
5 | 0.019041 | 0.018264 |
結果を検証すると1回目は最初に呼んだ方が余計に時間がかかるみたいなのでこの値は除外するとして、基本的に type A の方が処理速度が速いという結論になりました。
次にどの様な部分が処理速度の違いに影響したのかアセンブリコードにて確認。
調べてみると当たり前ですが加算処理の部分に違いが有りました。
type A は
adds r1, #1
type B は
adds r2, r1, #1
というコードに成ってました。ここでこのコードから速度の違いを解説出来たらカッコイイんですが自分の能力だとちょっと解析出来ませんでした(うーん、どちらもサイクル数に違い無いハズだしなぁ)。詳しい方に解説のコメントなど頂けると嬉しいです。コメント頂きました。ありがとうございます!!
(追記)
Justy
多分、typeBはメモリへのストア命令に使ったレジスタ r2が、次のステップでメモリからのロード先に指定されているのでストールし、その分遅くなったのだと思います。
(追記ここまで)
一応、気を付けて書いてはいますがベンチマークに使ったプログラムの記述などにも問題が有るようでしたら指摘して頂けると嬉しいです。
アセンブリのコードも記載しておきます。
【type A】
.thumb_func "-[ViewController typeA]" "-[ViewController typeA]": Ltmp0: Lfunc_begin0: .loc 1 16 0 push {r4, r7, lr} add r7, sp, #4 sub sp, #44 mov r4, sp bic r4, r4, #7 mov sp, r4 movs r2, #0 movt r2, #0 str r0, [sp, #40] str r1, [sp, #36] .loc 1 22 5 prologue_end Ltmp1: str r2, [sp, #8] bl _CFAbsoluteTimeGetCurrent vmov d16, r0, r1 vstr.64 d16, [sp, #16] .loc 1 23 5 ldr r0, [sp, #8] str r0, [sp, #28] .loc 1 24 5 ldr r0, [sp, #8] str r0, [sp, #32] LBB0_1: movw r0, #16960 movt r0, #15 ldr r1, [sp, #32] cmp r1, r0 bge LBB0_4 ldr.n r0, LCPI0_1 LPC0_1: add r0, pc .loc 1 25 9 Ltmp2: ldr r1, [sp, #28] adds r1, #1 str r1, [sp, #28] ldr r2, [sp, #32] lsls r2, r2, #2 add r0, r2 str r1, [r0] Ltmp3: .loc 1 24 27 ldr r0, [sp, #32] adds r0, #1 str r0, [sp, #32] b LBB0_1 LBB0_4: movs r0, #0 movt r0, #0 Ltmp4: .loc 1 27 5 str r0, [sp, #4] bl _CFAbsoluteTimeGetCurrent vmov d16, r0, r1 vldr.64 d17, [sp, #16] vsub.f64 d16, d16, d17 vmov r1, r2, d16 movw r0, :lower16:(L__unnamed_cfstring_-(LPC0_2+4)) movt r0, :upper16:(L__unnamed_cfstring_-(LPC0_2+4)) LPC0_2: add r0, pc blx _NSLog .loc 1 29 5 ldr r0, [sp, #4] str r0, [sp, #32] LBB0_5: movw r0, #16960 movt r0, #15 ldr r1, [sp, #32] cmp r1, r0 bge LBB0_8 vmov.f64 d16, #5.000000e-01 ldr.n r0, LCPI0_0 LPC0_0: add r0, pc .loc 1 30 9 Ltmp5: ldr r1, [sp, #32] lsls r1, r1, #2 add r0, r1 ldr r0, [r0] vmov s0, r0 vcvt.f64.s32 d17, s0 vmul.f64 d16, d17, d16 vldr.32 s0, [sp, #12] vcvt.f64.f32 d17, s0 vadd.f64 d16, d17, d16 vcvt.f32.f64 s0, d16 vstr.32 s0, [sp, #12] Ltmp6: .loc 1 29 27 ldr r0, [sp, #32] adds r0, #1 str r0, [sp, #32] b LBB0_5 Ltmp7: LBB0_8: .loc 1 32 5 vldr.32 s0, [sp, #12] vcvt.f64.f32 d16, s0 vmov r1, r2, d16 movw r0, :lower16:(L__unnamed_cfstring_2-(LPC0_3+4)) movt r0, :upper16:(L__unnamed_cfstring_2-(LPC0_3+4)) LPC0_3: add r0, pc blx _NSLog .loc 1 33 1 subs r4, r7, #4 mov sp, r4 pop {r4, r7, pc} .align 2 LCPI0_0: .long _x_array-(LPC0_0+4) .align 2 LCPI0_1: .long _x_array-(LPC0_1+4) Ltmp8: Lfunc_end0:
【type B】
.thumb_func "-[ViewController typeB]" "-[ViewController typeB]": Ltmp10: Lfunc_begin1: .loc 1 35 0 push {r4, r7, lr} add r7, sp, #4 sub sp, #44 mov r4, sp bic r4, r4, #7 mov sp, r4 movs r2, #0 movt r2, #0 str r0, [sp, #40] str r1, [sp, #36] .loc 1 41 5 prologue_end Ltmp11: str r2, [sp, #8] bl _CFAbsoluteTimeGetCurrent vmov d16, r0, r1 vstr.64 d16, [sp, #16] .loc 1 42 5 ldr r0, [sp, #8] str r0, [sp, #28] .loc 1 43 5 ldr r0, [sp, #8] str r0, [sp, #32] LBB1_1: movw r0, #16960 movt r0, #15 ldr r1, [sp, #32] cmp r1, r0 bge LBB1_4 ldr.n r0, LCPI1_1 LPC1_1: add r0, pc .loc 1 44 9 Ltmp12: ldr r1, [sp, #28] adds r2, r1, #1 str r2, [sp, #28] ldr r2, [sp, #32] lsls r2, r2, #2 add r0, r2 str r1, [r0] Ltmp13: .loc 1 43 27 ldr r0, [sp, #32] adds r0, #1 str r0, [sp, #32] b LBB1_1 LBB1_4: movs r0, #0 movt r0, #0 Ltmp14: .loc 1 46 5 str r0, [sp, #4] bl _CFAbsoluteTimeGetCurrent vmov d16, r0, r1 vldr.64 d17, [sp, #16] vsub.f64 d16, d16, d17 vmov r1, r2, d16 movw r0, :lower16:(L__unnamed_cfstring_4-(LPC1_2+4)) movt r0, :upper16:(L__unnamed_cfstring_4-(LPC1_2+4)) LPC1_2: add r0, pc blx _NSLog .loc 1 48 5 ldr r0, [sp, #4] str r0, [sp, #32] LBB1_5: movw r0, #16960 movt r0, #15 ldr r1, [sp, #32] cmp r1, r0 bge LBB1_8 vmov.f64 d16, #5.000000e-01 ldr.n r0, LCPI1_0 LPC1_0: add r0, pc .loc 1 49 9 Ltmp15: ldr r1, [sp, #32] lsls r1, r1, #2 add r0, r1 ldr r0, [r0] vmov s0, r0 vcvt.f64.s32 d17, s0 vmul.f64 d16, d17, d16 vldr.32 s0, [sp, #12] vcvt.f64.f32 d17, s0 vadd.f64 d16, d17, d16 vcvt.f32.f64 s0, d16 vstr.32 s0, [sp, #12] Ltmp16: .loc 1 48 27 ldr r0, [sp, #32] adds r0, #1 str r0, [sp, #32] b LBB1_5 Ltmp17: LBB1_8: .loc 1 51 5 vldr.32 s0, [sp, #12] vcvt.f64.f32 d16, s0 vmov r1, r2, d16 movw r0, :lower16:(L__unnamed_cfstring_2-(LPC1_3+4)) movt r0, :upper16:(L__unnamed_cfstring_2-(LPC1_3+4)) LPC1_3: add r0, pc blx _NSLog .loc 1 52 1 subs r4, r7, #4 mov sp, r4 pop {r4, r7, pc} .align 2 LCPI1_0: .long _x_array-(LPC1_0+4) .align 2 LCPI1_1: .long _x_array-(LPC1_1+4) Ltmp18: Lfunc_end1: