ifとswitchの処理時間(C++)

同じことを実行するプログラムでも、条件判断「if」と「switch」で処理時間が違いますよというお話。


1. ifを使ったサンプルプログラム

#include "stdafx.h"


int a = 3, b = 0;


int _tmain(int argc, _TCHAR* argv[])
{
    if ( a == 0 )
        b = 1;
    if ( a == 1 )
        b = 2;
    if ( a == 2 )
        b = 3;
    if ( a == 3 )
        b = 4;
    else
        b = 5;
    return 0;
}

2. switchを使ったサンプルプログラム

#include "stdafx.h"


int a = 3, b = 0;


int _tmain(int argc, _TCHAR* argv[])
{
    switch(a)
    {
        case 0: b = 1; break;
        case 1: b = 2; break;
        case 2: b = 3; break;
        case 3: b = 4; break;
        default: b = 5; break;
    }
    return 0;
}

アセンブリコードに変換する。
1-a. ifプログラム

; 11 : if ( a == 0 )

 0001e 83 3d 00 00 00 00 00 cmp DWORD PTR ?a@@3HA, 0 ; a
 00025 75 0a jne SHORT $LN5@wmain

; 12 : b = 1;

 00027 c7 05 00 00 00 00 01 00 00 00 mov DWORD PTR ?b@@3HA, 1 ; b
$LN5@wmain:

; 13 : if ( a == 1 )

 00031 83 3d 00 00 00 00 01 cmp DWORD PTR ?a@@3HA, 1 ; a
 00038 75 0a jne SHORT $LN4@wmain

; 14 : b = 2;

 0003a c7 05 00 00 00 00 02 00 00 00 mov DWORD PTR ?b@@3HA, 2 ; b
(以下、省略)

1) 比較命令cmpを使ってaの値が0の時、「ゼロ・フラグ」を「1」にする。(値が異なる場合は、「ゼロ・フラグ」は「0」)
2) 条件付きジャンプjneで分岐処理をする。
 jne*1は、「ゼロ・フラグ」が「0」の時(つまり、aが0でない時)に指定したアドレス($LN5@wmain)に制御を移す。
 「ゼロ・フラグ」が「1」の時(a==0の時)は、mov命令でbに1を代入する。
ifの場合は、上記の判断を繰り返し実行している。

2-a. swaitchプログラム

; 11 : switch(a)
 00038 ff 24 8d 00 00 00 00 jmp DWORD PTR $LN10@wmain[ecx*4]
$LN5@wmain:
(中略)


; 12 : {
; 13 : case 0: b = 1; break;

 0003f c7 05 00 00 00 00 01 00 00 00 mov DWORD PTR ?b@@3HA, 1 ; b
 00049 eb 2e jmp SHORT $LN6@wmain
(中略)


$LN10@wmain:
 00084 00 00 00 00 DD $LN5@wmain
 00088 00 00 00 00 DD $LN4@wmain
 0008c 00 00 00 00 DD $LN3@wmain
 00090 00 00 00 00 DD $LN2@wmain

1) ecxレジスタの値を4倍して、$LN10@wmainの値を足して、次処理のアドレスを取得する。
 ecxレジスタには、switchの条件となる値が入る。a=1の場合、1を4倍し$LN10@wmainの値を足し、アドレスを取得する。
2) $LN10@wmainラベル内の該当アドレス(1)で得られたアドレス)にジャンプする。
3) DDに続けて書かれているラベルの処理が実行される。


ifでは、条件がヒットするまで条件判断を繰り返す。
それに対し、switchでは分岐内容をテーブルに展開し、条件判断は掛け算と足し算でアドレスを特定して処理する。
そのため、条件判断が多くなるほどifの処理時間は遅くなる。
また、switchでは先頭と後ろにある判断項目で、ジャンプに要する時間は変わらない。


ifとswitch*2以外にも、同じことを実行しているのに処理効率が違うコードがあると思います。
今回比較したように、アセンブリコードレベルで処理を比較するのも面白いかもというお話でした。

*1:Not Equalの時、Jumpする

*2:ifとswitchだと感覚的にswitchの方が効率が良さそうだと分かりますが、比較してみました。