Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Should multiplication by integer be avoided in favour of division for performance reasons?

by vr (Curate)
on Nov 28, 2019 at 23:55 UTC ( [id://11109404]=perlquestion: print w/replies, xml ) Need Help??

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

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.

  • Comment on Should multiplication by integer be avoided in favour of division for performance reasons?
  • Select or Download Code

Replies are listed 'Best First'.
Re: Should multiplication by integer be avoided in favour of division for performance reasons?
by Anonymous Monk on Nov 29, 2019 at 01:44 UTC
    Use strings instead of subs and the benchmark numbers will be closer to reality

      Its your basic benchmark pitfall, the overhead overshadows the thing you're timing

      #!/usr/bin/perl -- 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 ]}, }); cmpthese( -2, { 1 => sub {[ map $_ * 4, @a ]}, 2 => sub {[ map $_ / (1/4), @a ]}, }); cmpthese( -2, { 1 => sub {[ map $_ * 4, @a ]}, 2 => sub {[ map $_ / (1/4), @a ]}, }); cmpthese( -2, { 1 => '[ map $_ * 4, @a ]', 2 => '[ map $_ / (1/4), @a ]', }); cmpthese( -2, { 1 => '[ map $_ * 4, @a ]', 2 => '[ map $_ / (1/4), @a ]', }); cmpthese( -2, { 1 => '[ map $_ * 4, @a ]', 2 => '[ map $_ / (1/4), @a ]', }); __END__

      But as you can see, there is no difference between the two

          Rate    1    2
      1 1.73/s   -- -18%
      2 2.11/s  22%   --
          Rate    1    2
      1 1.70/s   -- -19%
      2 2.11/s  24%   --
          Rate    1    2
      1 1.75/s   -- -17%
      2 2.10/s  20%   --
             Rate   1   2
      1 2371151/s  -- -3%
      2 2438111/s  3%  --
             Rate   2   1
      2 2272556/s  -- -8%
      1 2473993/s  9%  --
             Rate   2   1
      2 2140327/s  -- -8%
      1 2313868/s  8%  --
      

        I know there are overheads to calling subs, but did not think they were sufficiently large that inline code runs millions of times per second while code refs run at fewer than ten times per second.

        Purely out of curiosity, are the process and overheads documented anywhere? I can think of things like setting up @_ and other bookkeeping processes, but perhaps there are other steps.

        There is no @main::a. You are benchmarking couple of no-ops.

        use strict; use warnings; use Benchmark qw/ cmpthese timeit /; our @b = my @a = map log, 1..1e6; timeit( 1, 'print "\$#a = $#a\n";' ); timeit( 1, 'print "\$#b = $#b\n";' ); cmpthese( -2, { a => '[ map $_ * 4, @a ]', b => '[ map $_ * 4, @b ]', }); cmpthese( -2, { 1 => '[ map $_ * 4, @b ]', 2 => '[ map $_ / (1/4), @b ]', }); __END__ $#a = -1 $#b = 999999 Rate b a b 7.70/s -- -100% a 10680591/s 138780824% -- Rate 1 2 1 7.76/s -- -28% 2 10.7/s 39% --

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11109404]
Approved by choroba
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (4)
As of 2024-04-20 04:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found