in reply to Re: Simple Switch statement
in thread Simple Switch statement

For scoping reasons, I would change:
sub switch{ eval{ goto "case_$_[0]" } or goto default; } for my $expr ( 1 .. 10, 'fred' ) { switch( $expr ); {
to:
my $switch = sub { eval{ goto "case_$_[0]" } or goto default; }; for my $expr ( 1 .. 10, 'fred' ) { $switch->( $expr ); {
but other than that, I must say I like this idiom as well. If you're not interested in performance, that is. Because the performance of the switch like this, is abysmal compared to an identical if/elsif/else structure, as this little benchmark shows:
use strict; use Benchmark; timethese( 50000,{ switch => sub { sub switch{ eval{ goto "case_$_[0]" } or goto default; } for my $expr ( 1 .. 10, 'fred' ) { switch( $expr ); { case_1: print STDERR '1'; last; case_2: print STDERR '2'; last; case_3: print STDERR '3'; last; case_4: print STDERR '4'; last; case_5: ; case_6: print STDERR '5 or 6'; last; case_fred: print STDERR 'fred'; last; default: print STDERR "default"; } } }, if => sub { for my $expr ( 1 .. 10, 'fred' ) { if ($expr eq '1') {print STDERR '1'} elsif ($expr eq '2') {print STDERR '2'} elsif ($expr eq '3') {print STDERR '3'} elsif ($expr eq '4') {print STDERR '4'} elsif ($expr eq '5' or $expr eq '6') {print STDERR '5 or 6'} elsif ($expr eq 'fred') {print STDERR 'fred'} else {print STDERR "default"} } }, } );
Please note that I changed the print statements to "print STDERR" so that I could send them to the bitbucket. The result of the benchmark:
Benchmark: timing 50000 iterations of if, switch if: 7 wallclock secs ( 5.41 usr + 0.00 sys = 5.41 CPU) @ 92 +42.14/s (n=50000) switch: 43 wallclock secs (39.85 usr + 0.00 sys = 39.85 CPU) @ 12 +54.71/s (n=50000)

So the switch structure is at least 7 times as a comparable id/elsif/else structure. So don't put this in very deep and tight loops!

Liz

Edit by tye, change PRE to CODE around long lines

Replies are listed 'Best First'.
Performance - Re: Re: Re: Simple Switch statement
by knexus (Hermit) on Sep 14, 2003 at 15:06 UTC
    Performance or lack of it in the switch:

    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:

      Switch.pm makes code look elegant, but imposes significant performance penalty. I learnt this the hard way. Here is the lesson, hopefully it should help others.

      Using Switch:

      $ cat switch.pl #!/usr/local/bin/perl use Switch; my $letter = "c"; switch($letter) { case "a" {print "1"} case "b" {print "2"} case "c" {print "3"} }

      Profiling this program with Devel::NYTProf, gave some valuable insight:

      ______Source_Code_Files_—_ordered_by_exclusive_time_then_name__ +_____ |Stmts|Exclusive|Reports |Source File + | |_____|Time_____|__________________|__________________________________ +_____| |3508_|324ms____|line • block |Text/Balanced.pm_______________________ +| |542__|147ms____|line • block |Switch.pm______________________________ +| |77___|32.1ms___|line • block |Exporter/Heavy.pm______________________ +| |52___|31.7ms___|line • block |Config_heavy.pl________________________ +| |132__|30.9ms___|line • block |warnings/register.pm___________________ +| |73___|22.2ms___|line • block |DynaLoader.pm__________________________ +| |39___|29.2ms___|line • block |SelfLoader.pm_(including_1_string_eval) +| |27___|15.5ms___|line • block |AutoLoader.pm__________________________ +| |184__|13.3ms___|line • block |overload.pm____________________________ +| |83___|12.9ms___|line • block |vars.pm________________________________ +| |170__|12.2ms___|line • block |Exporter.pm____________________________ +| |40___|10.4ms___|line • block |Config.pm______________________________ +| |12___|9.38ms___|line • block |Carp.pm________________________________ +| |25___|7.80ms___|line • block |Filter/Util/Call.pm____________________ +| |15___|7.77ms___|line • block |switch.pl______________________________ +| |25___|6.78ms___|line • block |version.pm_____________________________ +| |123__|4.36ms___|line • block |strict.pm______________________________ +| |34___|2.22ms___|line • block |warnings.pm____________________________ +| |26___|619µs___|line • block |feature.pm_____________________________| |1____|8µs_____|line • block |Config_git.pl__________________________| |5188_|720ms____|Total_(-4_statements_are_unaccounted_for)____________ +_____| |259__|36.0ms___|Average______________________________________________ +_____| |_____|12.9ms___|Median_______________________________________________ +_____| |_____|0.00852__|Deviation____________________________________________ +_____| Report produced by the NYTProf_4.04 Perl profiler, developed by Tim_Bu +nce and Adam_Kaplan.
      Though my program had 8 lines, a total of 5188 statements were processed!

      Using if-then-else:

      $ cat if-then-else.pl #!/usr/local/bin/perl my $letter = "c"; if ($letter eq "a") {print "1"; }elsif ($letter eq "b") {print "2"; }elsif ($letter eq "c") {print "3"; }
      Profiler output is:
      ____________________then_name_____________________ |Stmts|Exclusive|Reports |Source File | |_____|Time_____|__________________|_______________| |3____|13.0ms___|line • block |if-then-else.pl|

      Much better!

      Using a dispatch-table:

      #!/usr/local/bin/perl my $letter = "c"; my %h = ( "a" => sub{print "1"}, "b" => sub{print "2"}, "c" => sub{print "3"}, ); $h{$letter}->();
      And the profiler output:
      Source Code Files — ordered by exclusive time _____________________then_name_____________________ |Stmts|Exclusive|Reports |Source File | |_____|Time_____|__________________|________________| |5____|4.10ms___|line • block |dispatch-hash.pl|

      The best!!

      Of course, plain if-then-else and dispatch-hash are comparable. Depending on the use case, either may be chosen. However, it is clear that both of them are way better than Switch. For an in-production CGI application that I was tasked to improve performance of, replacing Switch with if-then-else gave a 150% improvement in performance. Many thanks to Tim Bunce and Adam Kaplan for writing that very useful tool - Devel::NYTProf.