adamZ88 has asked for the wisdom of the Perl Monks concerning the following question:

Monks, I have a conditional statement that checks if the value of a variable is not equal to two values stored in separate variables. If it is not equal to either variable, the program does some stuff. I now need to add an additional check. For now, I only have 1 additional check, but there is a potential for the checks to increase. What is the most efficient way of doing this? I feel that nested ifs are not the best way to achieve my goal. This is just assumption, but I think this would be slow and if the number of checks is so grand then management may become overwhelming. Thanks

my $sPhoneCapabilityCode = "00000490"; my $s3905CapabilityCode ="00000 +290"; #$sUnpackedCDPcap is Dynamically generated during the program if ($sUnpackedCDPcap ne $sPhoneCapabilityCode & $sUnpackedCDPcap ne $s +3905CapabilityCode ){ ##Do some stuff## }

Replies are listed 'Best First'.
Re: How to add more conditional statements in an efficient manner
by huck (Prior) on May 02, 2017 at 18:46 UTC

    There are a number of ways to do this, and a number of reasons to do it one way or another

    Using a lookup hash can be conceptually easy to understand but slower to run for small numbers of lookups.

    my %lookup; $lookup{"00000490"}=1; $lookup{"00000290"}=1; ... unless ($lookup{$sUnpackedCDPcap}) { ##Do some stuff## }
    For large sparse lookup tables this will be faster than a linear search tho

      Thanks Huck, If this is ideal for large lookup tables, what method would you recommend for say 5 lockups?

        It all depends on how many things you are looking up, and how many in the match tree.

        use strict; use warnings; testit(100000,5); testit(100000,50); testit(1000000,5); testit(1000000,50); sub testit { my $inputsize=shift; my $testn=shift; my @list; for my $i(1..$inputsize) { push @list,sprintf('%010d',$i); } my @lookfors; for my $i(1..$testn) { push @lookfors,sprintf('%010d',$i); } my %lookup; for my $lf (@lookfors){ $lookup{$lf}=1; } { my $type='hash'; my $st=time; my $ct=0; for my $test (@list) { unless ($lookup{$test}) {$ct++;} } my $et=time-$st; printf "%5s inputsize:%10d testn:%5d ct:%10d time:%3d \n",$type,$inp +utsize,$testn,$ct,$et; } { # approx an if/the/elsif/else tree my $type='list'; my $st=time; my $ct=0; tests: for my $test (@list) { for my $lf(@lookfors){ if ($test eq $lf) {next tests; } } $ct++; } my $et=time-$st; printf "%5s inputsize:%10d testn:%5d ct:%10d time:%3d \n",$type,$inp +utsize,$testn,$ct,$et; } }
        result
        hash inputsize: 100000 testn: 5 ct: 99995 time: 0 list inputsize: 100000 testn: 5 ct: 99995 time: 0 hash inputsize: 100000 testn: 50 ct: 99950 time: 0 list inputsize: 100000 testn: 50 ct: 99950 time: 1 hash inputsize: 1000000 testn: 5 ct: 999995 time: 0 list inputsize: 1000000 testn: 5 ct: 999995 time: 2 hash inputsize: 1000000 testn: 50 ct: 999950 time: 0 list inputsize: 1000000 testn: 50 ct: 999950 time: 10
        As you an see for a small number of items to test against it doesnt make much of a difference. I would use the hash just because the code is cleaner.

        'what method would you recommend for say 5 lockups?'

        I would contact my local police authority and ask them.

Re: How to add more conditional statements in an efficient manner (updated x2)
by haukex (Archbishop) on May 02, 2017 at 19:32 UTC

    For string eq and ne, I like huck's hash table idea. For the generic problem of testing something against multiple other things with the same condition, see threads like Testing multiple variables against the same criteria (IF statement). Just one of several Ways To Do It:

    use Perl6::Junction qw/all/; my $sPhoneCapabilityCode = "00000490"; my $s3905CapabilityCode ="00000290"; chomp( my $sUnpackedCDPcap = <STDIN> ); if ( $sUnpackedCDPcap ne all($sPhoneCapabilityCode,$s3905CapabilityCod +e) ) { print "Bingo!\n"; }

    Note I haven't benchmarked it*, but my feeling is this would probably be slower than hash lookups. If it is speed you're after, you might try Benchmarking different approaches. My gut feeling is that raw if ($x ne $y && $x ne $z && $x ne ... ) { and a regex (see e.g. this) might be fastest, along with hash lookups, followed by solutions using modules. But then again, don't get lost in premature optimizations either - one advantage of the example above is that the code reads very nicely :-)

    * Update: Interesting, hash lookup outperforms the others by a large margin (code is below):

    Rate junct regex raw hash junct 104/s -- -79% -87% -95% regex 504/s 386% -- -38% -74% raw 806/s 678% 60% -- -58% hash 1932/s 1763% 284% 140% --

    Update 2: Inspired by Marshall's post about ne vs. !=, if you add that test to the above benchmark, it turns out that hashes still outperform it by a factor of roughly 2. Of course, numeric comparisons may not apply in your case!

      This is a very helpful response! Thanx

Re: How to add more conditional statements in an efficient manner
by Marshall (Canon) on May 03, 2017 at 00:28 UTC
    When looking at the performance aspects of the OP's code, I'd like to point out a few things about the "if" statement that you may not be aware of:
    if ( $sUnpackedCDPcap ne $sPhoneCapabilityCode & $sUnpackedCDPcap ne $s3905CapabilityCode ) { ##Do some stuff## }
    Please refer to Perl Operators for more details.
    The "ne" is a string operator, not a numeric one. A numeric compare of two integer binary values is something that the CPU can do very,very quickly in a single ALU (Arithmetic and Logic Unit) operation. String comparisons are byte by byte comparisons. With your 8 character strings, it could be that eight ALU operations are required to establish string equality instead of just a single operation for a binary number. Also when comparing strings there is some loop overhead so we don't "run off the end", etc. Summary: comparing a binary number is much faster than comparing a string.

    Also look at the "&" operator. This is a bit-wise operation that "and's" two things together. Both sides of the "&" are evaluated then and'ed together.

    Binary "&&" performs a short-circuit, C-style logical AND operation. That is, if the left operand is false, the right operand is not even evaluated. This is different than the single '&' bitwise operator.

    In Perl numeric values start out as strings. The numeric representation of a string is not calculated unless that needs to happen. I think there are some Monks who understand Perl Guts well enough to explain what this "if" statement will do. I believe that the numeric values of these variables will be calculated and used in the evaluation of the "if". If another "if" clause is added, it will run much faster because say, $sPhoneCapabilityCode will already exist as a numeric value, not a string.

    if ( ($sUnpackedCDPcap != $sPhoneCapabilityCode) && ($sUnpackedCDPcap != $s3905CapabilityCode ) ) { ##Do some stuff## }
    I am very skeptical that a hash approach can beat a simple numeric "if" approach for small n, say <10. Byte by byte string operations like calculating the numeric hash key of a string are just inherently slower than a single operation that compares 2 numeric values together.

    I haven't done any bench marking, but I think the logic of my argument is compelling enough to warrant more investigation.

    Update: Adding some benchmark code to show difference between string and numeric "if" versions:

    #!usr/bin/perl use strict; use warnings; use Benchmark qw(:all); my $count = 100000000; # one hundred million cycles my $sUnpackedCDPcap = "00000491"; my $sPhoneCapabilityCode = "00000490"; my $s3905CapabilityCode ="00000290"; timethese ($count, { 'String_if' => \&string_if, 'Do_Nothing' => \&do_nothing, 'Numeric_if' => \&numeric_if, } ); sub do_nothing { #don't do anything return; } sub string_if { #$sUnpackedCDPcap is Dynamically generated during the program if ($sUnpackedCDPcap ne $sPhoneCapabilityCode & $sUnpackedCDPcap ne $s3905CapabilityCode ) { ##Do some stuff## } return; } sub numeric_if { if ( ( $sUnpackedCDPcap != $sPhoneCapabilityCode) && ( $sUnpackedCDPcap != $s3905CapabilityCode ) ) { ##Do some stuff## } return; } __END__ Benchmark: timing 100000000 iterations of Do_Nothing, Numeric_if, Stri +ng_if... Do_Nothing: 4 wallclock secs ( 4.03 usr + 0.00 sys = 4.03 CPU) @ 24 +801587.30/s (n=100000000) Numeric_if: 15 wallclock secs (16.44 usr + 0.00 sys = 16.44 CPU) @ 60 +83835.25/s (n=100000000) String_if: 25 wallclock secs (26.59 usr + 0.00 sys = 26.59 CPU) @ 376 +0388.07/s (n=100000000)
    The difference between "do nothing" and the other benchmarks is so small that I wouldn't worry much about it. If you are going to do this 10 times, nobody will care! If you are doing this 100 million times, then yes, somebody will care. Note that I added a "do_nothing" test case. This measures some of the overhead in the test harness and I find adding a test case like that to be useful to "set a bottom execution floor" that can be achieved. An explicit return in all subs normalized the return values to be the same amoungst subroutines.

    At the end of the day, we are comparing things which are very similar. I did not benchmark the hash solution... left to the reader. I just wanted to make the point that numeric vs string comparisons are faster.

      "I am very skeptical that a hash approach can beat a simple numeric "if" approach for small n, say <10. Byte by byte string operations like calculating the numeric hash key of a string are just inherently slower than a single operation that compares 2 numeric values together."

      It does not, but for most applications that call for Perl's qualities, the loss of speed is not even noticed. The OP used the word "efficient" in their title: efficient for whom? The computer or the developer? (rhetorical question)

      Thank you for the detailed response! I appreciate it.