強火で進め

このブログではプログラム関連の記事を中心に書いてます。

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: