switch为何高效率
在用switch的同时,为何能高效,又如何避免编译退化?
arm-linux-objdump的一个基础实验,你懂得。
int main(void) { 834c: e24dd008 sub sp, sp, #8 ; 0x8 int a = 4; 8350: e3a03004 mov r3, #4 ; 0x4 8354: e58d3004 str r3, [sp, #4] switch(a) 8358: e59d3004 ldr r3, [sp, #4] 835c: e3530005 cmp r3, #5 ; 0x5 8360: 908ff103 addls pc, pc, r3, lsl #2 8364: ea00001c b 83dc <main+0x90> 8368: ea000004 b 8380 <main+0x34> 836c: ea000007 b 8390 <main+0x44> 8370: ea00000a b 83a0 <main+0x54> 8374: ea00000d b 83b0 <main+0x64> 8378: ea000010 b 83c0 <main+0x74> //通过跳转的方式,空间换时间 837c: ea000013 b 83d0 <main+0x84> { case 0: a++; break; 8380: e59d3004 ldr r3, [sp, #4] 8384: e2833001 add r3, r3, #1 ; 0x1 8388: e58d3004 str r3, [sp, #4] 838c: ea000012 b 83dc <main+0x90> case 1: a++; break; 8390: e59d3004 ldr r3, [sp, #4] 8394: e2833001 add r3, r3, #1 ; 0x1 8398: e58d3004 str r3, [sp, #4] 839c: ea00000e b 83dc <main+0x90> case 2: a++; break; 83a0: e59d3004 ldr r3, [sp, #4] 83a4: e2833001 add r3, r3, #1 ; 0x1 83a8: e58d3004 str r3, [sp, #4] 83ac: ea00000a b 83dc <main+0x90> case 3: a++; break; 83b0: e59d3004 ldr r3, [sp, #4] 83b4: e2833001 add r3, r3, #1 ; 0x1 83b8: e58d3004 str r3, [sp, #4] 83bc: ea000006 b 83dc <main+0x90> case 4: a++; break; 83c0: e59d3004 ldr r3, [sp, #4] 83c4: e2833001 add r3, r3, #1 ; 0x1 83c8: e58d3004 str r3, [sp, #4] 83cc: ea000002 b 83dc <main+0x90> case 5: a++; break; 83d0: e59d3004 ldr r3, [sp, #4] 83d4: e2833001 add r3, r3, #1 ; 0x1 83d8: e58d3004 str r3, [sp, #4] default: break; } return 0; 83dc: e3a03000 mov r3, #0 ; 0x0 }
再来试一些不规则的case。
int main(void) { 834c: e24dd008 sub sp, sp, #8 ; 0x8 int a = 9; 8350: e3a03009 mov r3, #9 ; 0x9 8354: e58d3004 str r3, [sp, #4] switch(a) 8358: e59d3004 ldr r3, [sp, #4] 835c: e3530009 cmp r3, #9 ; 0x9 8360: 908ff103 addls pc, pc, r3, lsl #2 8364: ea000020 b 83ec <main+0xa0> 8368: ea000008 b 8390 <main+0x44> 836c: ea00000b b 83a0 <main+0x54> 8370: ea00000e b 83b0 <main+0x64> 8374: ea00001c b 83ec <main+0xa0> 8378: ea000018 b 83e0 <main+0x94> 837c: ea00001a b 83ec <main+0xa0> 8380: ea000012 b 83d0 <main+0x84> 8384: ea000018 b 83ec <main+0xa0> 8388: ea000017 b 83ec <main+0xa0> 838c: ea00000b b 83c0 <main+0x74> //发生了看似不规则的变化,跳转未按地址顺序排列 { case 0: a++; break; 8390: e59d3004 ldr r3, [sp, #4] 8394: e2833001 add r3, r3, #1 ; 0x1 8398: e58d3004 str r3, [sp, #4] 839c: ea000012 b 83ec <main+0xa0> case 1: a++; break; 83a0: e59d3004 ldr r3, [sp, #4] 83a4: e2833001 add r3, r3, #1 ; 0x1 83a8: e58d3004 str r3, [sp, #4] 83ac: ea00000e b 83ec <main+0xa0> case 2: a++; break; 83b0: e59d3004 ldr r3, [sp, #4] 83b4: e2833001 add r3, r3, #1 ; 0x1 83b8: e58d3004 str r3, [sp, #4] 83bc: ea00000a b 83ec <main+0xa0> case 9: a++; break; 83c0: e59d3004 ldr r3, [sp, #4] 83c4: e2833001 add r3, r3, #1 ; 0x1 83c8: e58d3004 str r3, [sp, #4] 83cc: ea000006 b 83ec <main+0xa0> case 6: a++; break; 83d0: e59d3004 ldr r3, [sp, #4] 83d4: e2833001 add r3, r3, #1 ; 0x1 83d8: e58d3004 str r3, [sp, #4] 83dc: ea000002 b 83ec <main+0xa0> case 4: a++; break; 83e0: e59d3004 ldr r3, [sp, #4] 83e4: e2833001 add r3, r3, #1 ; 0x1 83e8: e58d3004 str r3, [sp, #4] default: break; } return 0; 83ec: e3a03000 mov r3, #0 ; 0x0 }
事物总不是绝对的,switch也是如此。
int main(void) { 834c: e24dd010 sub sp, sp, #16 ; 0x10 int a = 90; 8350: e3a0305a mov r3, #90 ; 0x5a 8354: e58d300c str r3, [sp, #12] switch(a) 8358: e59d300c ldr r3, [sp, #12] 835c: e58d3004 str r3, [sp, #4] 8360: e59d3004 ldr r3, [sp, #4] 8364: e3530002 cmp r3, #2 ; 0x2 8368: 0a00001b beq 83dc <main+0x90> //退化成了 if ! 836c: e59d3004 ldr r3, [sp, #4] 8370: e3530002 cmp r3, #2 ; 0x2 8374: ca000006 bgt 8394 <main+0x48> 8378: e59d3004 ldr r3, [sp, #4] 837c: e3530000 cmp r3, #0 ; 0x0 8380: 0a00000d beq 83bc <main+0x70> 8384: e59d3004 ldr r3, [sp, #4] 8388: e3530001 cmp r3, #1 ; 0x1 838c: 0a00000e beq 83cc <main+0x80> 8390: ea000020 b 8418 <main+0xcc> 8394: e59d3004 ldr r3, [sp, #4] 8398: e3530006 cmp r3, #6 ; 0x6 839c: 0a000016 beq 83fc <main+0xb0> 83a0: e59d3004 ldr r3, [sp, #4] 83a4: e353005a cmp r3, #90 ; 0x5a 83a8: 0a00000f beq 83ec <main+0xa0> 83ac: e59d3004 ldr r3, [sp, #4] 83b0: e3530004 cmp r3, #4 ; 0x4 83b4: 0a000014 beq 840c <main+0xc0> 83b8: ea000016 b 8418 <main+0xcc> { case 0: a++; break; 83bc: e59d300c ldr r3, [sp, #12] 83c0: e2833001 add r3, r3, #1 ; 0x1 83c4: e58d300c str r3, [sp, #12] 83c8: ea000012 b 8418 <main+0xcc> case 1: a++; break; 83cc: e59d300c ldr r3, [sp, #12] 83d0: e2833001 add r3, r3, #1 ; 0x1 83d4: e58d300c str r3, [sp, #12] 83d8: ea00000e b 8418 <main+0xcc> case 2: a++; break; 83dc: e59d300c ldr r3, [sp, #12] 83e0: e2833001 add r3, r3, #1 ; 0x1 83e4: e58d300c str r3, [sp, #12] 83e8: ea00000a b 8418 <main+0xcc> case 90: a++; break; 83ec: e59d300c ldr r3, [sp, #12] 83f0: e2833001 add r3, r3, #1 ; 0x1 83f4: e58d300c str r3, [sp, #12] 83f8: ea000006 b 8418 <main+0xcc> case 6: a++; break; 83fc: e59d300c ldr r3, [sp, #12] 8400: e2833001 add r3, r3, #1 ; 0x1 8404: e58d300c str r3, [sp, #12] 8408: ea000002 b 8418 <main+0xcc> case 4: a++; break; 840c: e59d300c ldr r3, [sp, #12] 8410: e2833001 add r3, r3, #1 ; 0x1 8414: e58d300c str r3, [sp, #12] default: break; } return 0; 8418: e3a03000 mov r3, #0 ; 0x0 }
秘密原来就在于此……
0000834c <func>: int func(int a) { 834c: e24dd008 sub sp, sp, #8 ; 0x8 8350: e58d0004 str r0, [sp, #4] switch(a) 8354: e59d3004 ldr r3, [sp, #4] 8358: e3530009 cmp r3, #9 ; 0x9 835c: 908ff103 addls pc, pc, r3, lsl #2 ///看了下面的排列方式,也就清楚了这条指令的内涵 8360: ea000020 b 83e8 <func+0x9c> 8364: ea000008 b 838c <func+0x40> //0 8368: ea00000b b 839c <func+0x50> //1 836c: ea00000e b 83ac <func+0x60> //2 8370: ea00001c b 83e8 <func+0x9c> 8374: ea000018 b 83dc <func+0x90> //4 8378: ea00001a b 83e8 <func+0x9c> 837c: ea000012 b 83cc <func+0x80> //6 8380: ea000018 b 83e8 <func+0x9c> 8384: ea000017 b 83e8 <func+0x9c> 8388: ea00000b b 83bc <func+0x70> //9 { case 0: a++; break; 838c: e59d3004 ldr r3, [sp, #4] 8390: e2833001 add r3, r3, #1 ; 0x1 8394: e58d3004 str r3, [sp, #4] 8398: ea000012 b 83e8 <func+0x9c> case 1: a++; break; 839c: e59d3004 ldr r3, [sp, #4] 83a0: e2833001 add r3, r3, #1 ; 0x1 83a4: e58d3004 str r3, [sp, #4] 83a8: ea00000e b 83e8 <func+0x9c> case 2: a++; break; 83ac: e59d3004 ldr r3, [sp, #4] 83b0: e2833001 add r3, r3, #1 ; 0x1 83b4: e58d3004 str r3, [sp, #4] 83b8: ea00000a b 83e8 <func+0x9c> case 9: a++; break; 83bc: e59d3004 ldr r3, [sp, #4] 83c0: e2833001 add r3, r3, #1 ; 0x1 83c4: e58d3004 str r3, [sp, #4] 83c8: ea000006 b 83e8 <func+0x9c> case 6: a++; break; 83cc: e59d3004 ldr r3, [sp, #4] 83d0: e2833001 add r3, r3, #1 ; 0x1 83d4: e58d3004 str r3, [sp, #4] 83d8: ea000002 b 83e8 <func+0x9c> case 4: a++; break; 83dc: e59d3004 ldr r3, [sp, #4] 83e0: e2833001 add r3, r3, #1 ; 0x1 83e4: e58d3004 str r3, [sp, #4] default: break; } return a; 83e8: e59d3004 ldr r3, [sp, #4] }
最后再来一个极端的实验:
0000834c <func>: int func(int a) { 834c: e24dd008 sub sp, sp, #8 ; 0x8 8350: e58d0004 str r0, [sp, #4] switch(a) 8354: e59d3004 ldr r3, [sp, #4] 8358: e353003c cmp r3, #60 ; 0x3c //这里60是上限,至少要六个case,而且要有 case[0-2],否则编译退化为if 835c: 908ff103 addls pc, pc, r3, lsl #2 8360: ea000053 b 84b4 <func+0x168> 8364: ea00003b b 8458 <func+0x10c> //0 8368: ea00003e b 8468 <func+0x11c> //1 836c: ea000041 b 8478 <func+0x12c> //2 8370: ea00004f b 84b4 <func+0x168> 8374: ea00004b b 84a8 <func+0x15c> 8378: ea00004d b 84b4 <func+0x168> 837c: ea000045 b 8498 <func+0x14c> //6 8380: ea00004b b 84b4 <func+0x168> 8384: ea00004a b 84b4 <func+0x168> 8388: ea000049 b 84b4 <func+0x168> 838c: ea000048 b 84b4 <func+0x168> 8390: ea000047 b 84b4 <func+0x168> 8394: ea000046 b 84b4 <func+0x168> 8398: ea000045 b 84b4 <func+0x168> 839c: ea000044 b 84b4 <func+0x168> 83a0: ea000043 b 84b4 <func+0x168> 83a4: ea000042 b 84b4 <func+0x168> 83a8: ea000041 b 84b4 <func+0x168> 83ac: ea000040 b 84b4 <func+0x168> 83b0: ea00003f b 84b4 <func+0x168> 83b4: ea00003e b 84b4 <func+0x168> 83b8: ea00003d b 84b4 <func+0x168> 83bc: ea00003c b 84b4 <func+0x168> 83c0: ea00003b b 84b4 <func+0x168> 83c4: ea00003a b 84b4 <func+0x168> 83c8: ea000039 b 84b4 <func+0x168> 83cc: ea000038 b 84b4 <func+0x168> 83d0: ea000037 b 84b4 <func+0x168> 83d4: ea000036 b 84b4 <func+0x168> 83d8: ea000035 b 84b4 <func+0x168> 83dc: ea000034 b 84b4 <func+0x168> 83e0: ea000033 b 84b4 <func+0x168> 83e4: ea000032 b 84b4 <func+0x168> 83e8: ea000031 b 84b4 <func+0x168> 83ec: ea000030 b 84b4 <func+0x168> 83f0: ea00002f b 84b4 <func+0x168> 83f4: ea00002e b 84b4 <func+0x168> 83f8: ea00002d b 84b4 <func+0x168> 83fc: ea00002c b 84b4 <func+0x168> 8400: ea00002b b 84b4 <func+0x168> 8404: ea00002a b 84b4 <func+0x168> 8408: ea000029 b 84b4 <func+0x168> 840c: ea000028 b 84b4 <func+0x168> 8410: ea000027 b 84b4 <func+0x168> 8414: ea000026 b 84b4 <func+0x168> 8418: ea000025 b 84b4 <func+0x168> 841c: ea000024 b 84b4 <func+0x168> 8420: ea000023 b 84b4 <func+0x168> 8424: ea000022 b 84b4 <func+0x168> 8428: ea000021 b 84b4 <func+0x168> 842c: ea000020 b 84b4 <func+0x168> 8430: ea00001f b 84b4 <func+0x168> 8434: ea00001e b 84b4 <func+0x168> 8438: ea00001d b 84b4 <func+0x168> 843c: ea00001c b 84b4 <func+0x168> 8440: ea00001b b 84b4 <func+0x168> 8444: ea00001a b 84b4 <func+0x168> 8448: ea000019 b 84b4 <func+0x168> 844c: ea000018 b 84b4 <func+0x168> 8450: ea000017 b 84b4 <func+0x168> 8454: ea00000b b 8488 <func+0x13c> { case 0: a++; break; 8458: e59d3004 ldr r3, [sp, #4] 845c: e2833001 add r3, r3, #1 ; 0x1 8460: e58d3004 str r3, [sp, #4] 8464: ea000012 b 84b4 <func+0x168> case 1: a++; break; 8468: e59d3004 ldr r3, [sp, #4] 846c: e2833001 add r3, r3, #1 ; 0x1 8470: e58d3004 str r3, [sp, #4] 8474: ea00000e b 84b4 <func+0x168> case 2: a++; break; 8478: e59d3004 ldr r3, [sp, #4] 847c: e2833001 add r3, r3, #1 ; 0x1 8480: e58d3004 str r3, [sp, #4] 8484: ea00000a b 84b4 <func+0x168> case 60: a++; break; 8488: e59d3004 ldr r3, [sp, #4] 848c: e2833001 add r3, r3, #1 ; 0x1 8490: e58d3004 str r3, [sp, #4] 8494: ea000006 b 84b4 <func+0x168> case 6: a++; break; 8498: e59d3004 ldr r3, [sp, #4] 849c: e2833001 add r3, r3, #1 ; 0x1 84a0: e58d3004 str r3, [sp, #4] 84a4: ea000002 b 84b4 <func+0x168> case 4: a++; break; 84a8: e59d3004 ldr r3, [sp, #4] 84ac: e2833001 add r3, r3, #1 ; 0x1 84b0: e58d3004 str r3, [sp, #4] default: break; } return a; 84b4: e59d3004 ldr r3, [sp, #4] } 84b8: e1a00003 mov r0, r3 84bc: e28dd008 add sp, sp, #8 ; 0x8 84c0: e12fff1e bx lr
上面的case最大值取了60,若再增加,编译退化。
而对于字符 case 'a' ,switch的退化条件有些区别,本质还是一样,不妨自己动手一试。
只是简单的贴了些代码,重在能说明问题的关键,点到为止。
jPeTsKse:00000123
jDeAsSsUEL:00000123