iOSのプログラムを書く場合にはインクリメントは変数の前、後ろどちらの方が高速?(その2)
その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: