switch

根据网上博文(见参考),亲自确认了一下GCC对switch的优化,这里留个笔记。


1. 概述

GCC会根据switch具体列举值的情况进行一些优化:

* 当枚举的值较多且比较连续时,使用跳转表,O(1)。

* 当枚举的值跨度很大时,不使用跳转表,但是,会针对枚举值进行二分跳转,O(lgn)。


2. 使用跳转表的情况

int switch_test(int x)
{
        int res ;
        switch( x ){
                case 100 :
                        res = 1 ;
                        break ;
                case 102 :
                        res = 2 ;
                        break ;
                case 103 :
                        res = 3 ;
                        break ;
                case 104 :
                        res = 4 ;
                        break ;
                case 105 :
                        res = 5 ;
                        break ;
                case 106 :
                        res = 6 ;
                        break ;
                case 120:
                        res = 6 ;
                        break ;
        }
        return res ;
}


        .file   "switch.c"
        .text
.globl switch_test
        .type   switch_test,@function
switch_test:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        movl    8(%ebp), %eax
        subl    $100, %eax              # 减去最小值100,得到对应的索引下标
        movl    %eax, -8(%ebp)
        cmpl    $20, -8(%ebp)
        ja      .L2
        movl    -8(%ebp), %edx
        movl    .L10(,%edx,4), %eax     # 根据索引下标,跳转到对应的代码段
        jmp     *%eax
        .section        .rodata
        .align 4
        .align 4
.L10:                                   # 跳转表,从switch值最小到最大
        .long   .L3                     # 值100,对应代码分支为L3
        .long   .L2                     # 值101,没有列举该值,因此,走default分支,也就是L2
        .long   .L4
        .long   .L5
        .long   .L6
        .long   .L7
        .long   .L8
        .long   .L2                     # 下面是值107-119,没有列举该值,因此,都走default分支,也就是L2
        .long   .L2
        .long   .L2
        .long   .L2
        .long   .L2
        .long   .L2
        .long   .L2
        .long   .L2
        .long   .L2
        .long   .L2
        .long   .L2
        .long   .L2
        .long   .L2
        .long   .L9                     # 值120,对应代码分支为L9
        .text
.L3:
        movl    $1, -4(%ebp)
        jmp     .L2
.L4:
        movl    $2, -4(%ebp)
        jmp     .L2
.L5:
        movl    $3, -4(%ebp)
        jmp     .L2
.L6:
        movl    $4, -4(%ebp)
        jmp     .L2
.L7:
        movl    $5, -4(%ebp)
        jmp     .L2
.L8:
        movl    $6, -4(%ebp)
        jmp     .L2
.L9:
        movl    $6, -4(%ebp)
.L2:
        movl    -4(%ebp), %eax
        leave
        ret
.Lfe1:
        .size   switch_test,.Lfe1-switch_test
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

3. 不使用跳转表的情况

int switch_test(int x)
{
        int res ;
        switch( x ){
                case 100 :
                        res = 1 ;
                        break ;
                case 102 :
                        res = 2 ;
                        break ;
                case 103 :
                        res = 3 ;
                        break ;
                case 104 :
                        res = 4 ;
                        break ;
                case 105 :
                        res = 5 ;
                        break ;
                case 106 :
                        res = 6 ;
                        break ;
                case 1200 :             # 值由120,改为1200
                        res = 9 ;
                        break ;
        }
        return res ;
}

        .file   "switch.c"
        .text
.globl switch_test
        .type   switch_test,@function
switch_test:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        movl    8(%ebp), %eax
        movl    %eax, -8(%ebp)
        cmpl    $104, -8(%ebp)          # 先判断中间值104
        je      .L6
        cmpl    $104, -8(%ebp)          # 如果不是104,则根据值比104大或小进行跳转
        jg      .L12
        cmpl    $102, -8(%ebp)
        je      .L4
        cmpl    $102, -8(%ebp)
        jg      .L5
        cmpl    $100, -8(%ebp)
        je      .L3
        jmp     .L2
.L12:
        cmpl    $106, -8(%ebp)		
        je      .L8
        cmpl    $106, -8(%ebp)          # 同上,都根据值的大小进行进一步跳转,实现二分跳转
        jl      .L7
        cmpl    $1200, -8(%ebp)
        je      .L9
        jmp     .L2
.L3:
        movl    $1, -4(%ebp)
        jmp     .L2
.L4:
        movl    $2, -4(%ebp)
        jmp     .L2
.L5:
        movl    $3, -4(%ebp)
        jmp     .L2
.L6:
        movl    $4, -4(%ebp)
        jmp     .L2
.L7:
        movl    $5, -4(%ebp)
        jmp     .L2
.L8:
        movl    $6, -4(%ebp)
        jmp     .L2
.L9:
        movl    $9, -4(%ebp)
.L2:
        movl    -4(%ebp), %eax
        leave
        ret
.Lfe1:
        .size   switch_test,.Lfe1-switch_test
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"


4. 结论

当列举值超过2,且不能确定每个列举值出现的概率时,应当尽可能用switch代替if-else。


5. 参考

http://blog.chinaunix.net/link.php?url=http://blog.csdn.net%2Fxiaohan13916830%2Farchive%2F2004%2F08%2F17%2F76724.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值