use strict;
use warnings;
use Benchmark 'cmpthese';
my @a = map log, 1..1e6;
cmpthese( -2, {
1 => sub {[ map $_ * 4, @a ]},
2 => sub {[ map $_ / (1/4), @a ]},
});
__END__
Rate 1 2
1 7.70/s -- -27%
2 10.6/s 38% --
Fooling around with integers and floats (inspired by this question: Clean log_10 ?), I found (again!) unknown to me Perl behaviour, which can actually lead to performance issues. Maybe it's open secret and was described 20 years ago. Couldn't come up with good Google query, found nothing, hence this question/observation.
It looks like if scalar contains NV, and though it never has been IV, and another operand is IV, and operator is e.g. multiplication (and some other operators, but not, important, division), then Perl checks if this scalar can be promoted (or is it "demoted"?) to IV, and if so, then result will be IV.
>perl -MDevel::Peek -E "$x=2/1; Dump$x; $y=$x*2; Dump$x; Dump$y"
SV = NV(0xd75628) at 0xd75640
REFCNT = 1
FLAGS = (NOK,pNOK)
NV = 2
SV = PVNV(0xe26078) at 0xd75640
REFCNT = 1
FLAGS = (IOK,NOK,pIOK,pNOK)
IV = 2
NV = 2
PV = 0
SV = IV(0xd75a80) at 0xd75a90
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 4
Though it means some amount of extra work, it could be argued that Perl tries to do "a right thing". However, with arrays, this helpful (?) optimization becomes an anti-DWIM, I think. If array contains just a single element which "looks like integer" (log 1, above), then "optimization" is attempted for all elements. Try range 2..1e6, then there's no penalty. I understand, that (a) as it's said somewhere in FAQ, Perl is not well suited for number crunching, and (b), if properly profiled, difference between multiplication and division by reciprocal somewhere in real program will probably be tiny. Fun fact, nevertheless.