[Wellington-pm] comparison and assignment: insignificant optimisation

Ewen McNeill perl at ewen.mcneill.gen.nz
Mon Jun 16 03:42:37 PDT 2014


On 16/06/14 21:59, Richard Hector wrote:
> I often find it necessary to do some comparison, then do something with
> one of the operands. Here's a common example:
>
> sub max_length { [...]
>
> What irritates me is that the length of the string is evaluated twice.

That particular one can be optimised by using map(), and further 
optimised by using List::Util max():

use List::Util qw(max);

sub max_length {
     return max(map { length($_) } @_);
}

Although you're trading memory (array of all lengths of strings) for 
time (calculating the lengths twice for any longer ones) and avoiding an 
explicit temporary variable.  (A list pre-sorted by length will be 
pessimal for your original algorithm; a list reverse sorted by length 
will be optimal, since it'll only calculate the length of the first 
element twice, and then the rest will be shorter so not re-calculated.)

Some quick testing suggests this might work to avoid the storage 
requirement:

use List::Util qw(max reduce);

sub max_length {
     return reduce { max($a, length($b)) } 0, @_;
}

(although it also gives me warnings of $main::a and $main::b only being 
used once).  OTOH, you're going to be calling max() a bunch more times 
on a long list, so function call overhead might hurt -- explicitly using 
the ternary operator might help, but you'd have to use a temporary 
variable to avoid length($b) being evaluated twice... which is the 
problem we started with.

FWIW, in your example if the optimiser knows that length($foo) is 
constant for a constant input then it should be able to peephole 
optimise it with a temporary variable, so it's only calculated once.  I 
couldn't quickly find out whether Perl's peephole optimiser (peep()) 
does that or not.

I guess my point here is that it may be premature optimisation unless 
you have a performance problem.  My concern would be readability I 
think, and I'd tend to use something with max() for that reason.

> I was wondering if there was a simpler way - maybe something like one of
> those action-assignment operators, like += or ||=.

AFAICT Perl doesn't have a built in "assign if greater than" operator 
:-)  $foo = max($foo, $bar) is about the closest you get, and even then 
you need to import max.

Ewen


More information about the Wellington-pm mailing list