This really caught me by surprise, as to me it just seems counter intuitive that indexed jumps would be slower, especially by such a huge margin, but hey this is perl not 'C', what do I know? ;-)
So, I took the code liz provided in post, which was so kindly benchmarked. I removed the prints to reduce dilution effect of typically long operations like print and replaced them with just an assignment; bart addressed this as well here. The following is a summay of things I looked at and the results:
1. I found that using the 'Default' case had a huge impact on performance. Modifying the code to use only non-default case values brought the performance gap down to around ~4:1. My guess is that this is related to the error handling in eval.
Anyhow, if the code can be written to rarely (if at all) use the default case, it's not too bad, but it still looses out to if/else. Note: Because this had such a large effect, I kept this change in all the remaining tests to avoid dilution of the results.
2. I found that the eval call itself contributed a little to the slower performance. When I modified the code to just goto LABEL, the ratio dropped to ~3:1. However, this would not allow for default handling (at least I can't do it). So, depending on needs, this may or may not be an option. IMHO, it seems that omitting eval probably does not help enoguh considering the code will be less robust.
3. Similarly, I checked to see if making a sub call vs. direct goto had much impact. This had about the same effect dropping it to ~3:1
4. What's the effect of the string operation to produce the LABEL? Yah, I know, this is needed for this idom to work. However, I thought it might prove interesting to know what it is anyhow. So, I just hardcoded a jump to one label for this test. This did account for quite a bit, dropping the gap to ~2:1. Now, I accidently discovered something suprizing, at least to me. I noticed that the distance to the respective label had an impact on the performance.
Huh? Jumping directly to 'case_1:' was always faster than jumping to any label beyond it. I added cases to get case_1: to case_20: sequentially. In my tests, jumping to case_1 was ~2:1 over if/else and jumping directly to case_20 was ~3:1. Just guessing why this would be, I thought it might be related to perl doing a sequential search or something, looking for the label. Dunno.
Anyhow, if you care, put the more heavily called cases first.
Finally: I suppose if you want to use this Switch approach in heavily used tight loops there are some options to bring performance in line. I know I will just try to avoid the 'default' case use. Also, there are probably other things I overlooked, these are just the ones I looked at.
I hope this is helpful.
CODE
Here's the code I was using in the above cases. I just left the different mods in, uncommenting which ever item I wanted to test.
#!/usr/bin/perl -w use strict; use Benchmark; my $lastCase; timethese( 50000,{ switch => sub { sub switch{ eval{ goto "case_$_[0]" } or goto default; # goto 'case_'.$_[0]; # goto case_1; # goto case_20; } for my $expr ( 1 .. 10, 'fred' ) { switch( $expr ); # switch: goto 'case_'.$expr; # switch: goto case_1; # switch: goto case_20; # switch($expr); # switch: eval{ goto "case_$expr" } or goto default; { case_1: $lastCase = '1'; last; case_2: $lastCase = '2'; last; case_3: $lastCase = '3'; last; case_4: $lastCase = '4'; last; case_5: $lastCase = '5'; last; case_6: $lastCase = '6'; last; case_7: $lastCase = '7'; last; case_8: $lastCase = '8'; last; case_9: $lastCase = '9'; last; case_10: $lastCase = '10'; last; case_11: $lastCase = '11'; last; case_12: $lastCase = '12'; last; case_13: $lastCase = '13'; last; case_14: $lastCase = '14'; last; case_15: $lastCase = '15'; last; case_16: $lastCase = '16'; last; case_17: $lastCase = '17'; last; case_18: $lastCase = '18'; last; case_19: $lastCase = '19'; last; case_20: $lastCase = '20'; last; case_fred: $lastCase = 'fred'; last; default: $lastCase = "default"; } } }, if => sub { for my $expr ( 1 .. 10, 'fred' ) { if ($expr eq '1') {$lastCase = '1'} elsif ($expr eq '2') {$lastCase = '2'} elsif ($expr eq '3') {$lastCase = '3'} elsif ($expr eq '4') {$lastCase = '4'} elsif ($expr eq '5') {$lastCase = '5'} elsif ($expr eq '6') {$lastCase = '6'} elsif ($expr eq '7') {$lastCase = '7'} elsif ($expr eq '8') {$lastCase = '8'} elsif ($expr eq '9') {$lastCase = '9'} elsif ($expr eq '10') {$lastCase = '10'} elsif ($expr eq '11') {$lastCase = '11'} elsif ($expr eq '12') {$lastCase = '12'} elsif ($expr eq '13') {$lastCase = '13'} elsif ($expr eq '14') {$lastCase = '14'} elsif ($expr eq '15') {$lastCase = '15'} elsif ($expr eq '16') {$lastCase = '16'} elsif ($expr eq '17') {$lastCase = '17'} elsif ($expr eq '18') {$lastCase = '18'} elsif ($expr eq '19') {$lastCase = '19'} elsif ($expr eq '20') {$lastCase = '20'} elsif ($expr eq 'fred') {$lastCase = 'fred'} else {$lastCase = "default"} } }, } );
In reply to Performance - Re: Re: Re: Simple Switch statement
by knexus
in thread Simple Switch statement
by knexus
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |