Logically And Yourself (&&=)

I ran into a situation today where I was trying to see whether any exceptions were thrown when evaluating a list of objects. In this particular case I didn’t want the exceptions to short circuit the loop, I just needed to know if any were thrown as we iterated over the list. So I wrote this benchmark to see which of two approaches would be faster.


use strict;
use Ouch;
use Benchmark qw(:all);

my @list = (1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1);

timethese(10000, {
    '&&=' => sub {
        my $is_proofed = 1;
        foreach my $x (@list) {
            $is_proofed &&= eval { $x ? 1 : ouch(500, 'bad stuff') };
        }   
    },  
    '$@' => sub {
        my $is_proofed = 1;
        foreach my $x (@list) {
            eval { $x ? 1 : ouch(500, 'bad stuff') };
            if ($@) {
                $is_proofed = 0;
            }   
        }   
    }   
});   

The more exceptions I introduce to the list the worse the $@ approach got, but the &&= approach remained consistently fast. Here are the Benchmark results with just 2 exceptions in the list:

Benchmark: timing 10000 iterations of &&=, $@...
       &&=:  3 wallclock secs ( 2.75 usr +  0.00 sys =  2.75 CPU) @ 3636.36/s (n=10000)
       $@ :  6 wallclock secs ( 5.63 usr +  0.01 sys =  5.64 CPU) @ 1773.05/s (n=10000)    

So if you find yourself needing to do a bunch of comparisons such as this, remember that &&= is your friend.

[From my blog.]

5 Comments

&&= is one of my favorite Perl operators.
I giggle every time I have to use it.

My usual use case is replacing an existing value with something else (e.g. a string with the corresponding Path::Class::File object).

This is because the eval block isn't getting executed after the 4th iteration in your &&= case. You're effectively almost short-circuiting the loop.

You could achieve a similar effect by inserting next unless $is_proofed; before the eval block in your $@ case.

Consider also `List::Util::all` or `::any`, which have similar shortcircuiting logic in them

I love interesting behavior with the binary assignments. This one is quite nice.

I expanded this benchmark in a gist (ouch.pl).

There are a few things to note.

1. These sorts of benchmarks often show the performance in a particular situation. People should try other situations too, such as the only error being at the end of the list, or the first element, and so on. In the worst cases, the performance is the same.

1a. The trick is to figure out what the likely situations are in your application. No particular approach is good universally.

2. Add some controls to see how much the non-interesting parts of the problem affect the time, such as the looping and the call to ouch().

3. If you only wanted to find the first problem, you could use last in the $@ case. That's almost the same performance, and as you know, easier for newbies to understand what you are doing.

Good catch! &= should be used instead.

Leave a comment

About JT Smith

user-pic My little part in the greater Perl world.