Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl list interpolation performance

Tags:

list

perl

Background

Perldoc for List::Util suggests that some uses of map may be replaced by reduce in order to avoid creating unnecessary intermadiate list:

For example, to find the total length of the all the strings in a list, we could use

$total = sum map { length } @strings;

However, this produces a list of temporary integer values as long as the original list of strings, only to reduce it down to a single value again. We can compute the same result more efficiently by using reduce with a code block that accumulates lengths by writing this instead as:

$total = reduce { $a + length $b } 0, @strings;

That makes sense. However, reduce in order to work in this example needs "identity value", that would be prepended to input list:

$total = reduce { $a + length $b } 0, @strings;
#                                  ^^^^^^^^^^^

That makes me think, doesn't 0, @strings create a new list, thus offset any gains from not creaing list in map?

Question

How does list interpolation ($scalar, @list) work in Perl? Does it involve copying elements from source list or is it done in some smarter way? My simple benchmark suggests copying taking place:

use strict;
use warnings;
use Benchmark qw/cmpthese/;

my @a1 = 1..10;
my @a2 = 1..100;
my @a3 = 1..1000;
my @a4 = 1..10000;
my @a5 = 1..100000;
my @a6 = 1..1000000;

cmpthese(10000, {
    'a1' => sub { my @l = (0, @a1); },
    'a2' => sub { my @l = (0, @a2); },
    'a3' => sub { my @l = (0, @a3); },
    'a4' => sub { my @l = (0, @a4); },
    'a5' => sub { my @l = (0, @a5); },
    'a6' => sub { my @l = (0, @a6); },
});

Results:

            (warning: too few iterations for a reliable count)
        Rate       a6       a5       a4       a3       a2       a1
a6    17.6/s       --     -90%     -99%    -100%    -100%    -100%
a5     185/s     952%       --     -90%     -99%    -100%    -100%
a4    1855/s   10438%     902%       --     -90%     -99%    -100%
a3   17857/s  101332%    9545%     862%       --     -91%     -98%
a2  200000/s 1135940%  107920%   10680%    1020%       --     -80%
a1 1000000/s 5680100%  540000%   53800%    5500%     400%       --

Bonus question: If my assumptions are correct (i.e. 0, @strings creates a new list), does replacing map with reduce make sense?

like image 757
el.pescado - нет войне Avatar asked Dec 23 '22 16:12

el.pescado - нет войне


1 Answers

doesn't 0, @strings create a new list

Not really. If you decompile the code, it's just one additional SVOP.

But you're measuring the wrong thing. The values are flattened and passed into the map or reduce subroutine in both cases!

The documentation is talking about what happens inside the subroutine. map creates a list of as many input values and returns them, and then sum takes the list and condenses it into a value. The return list is ephemeral and is not represented directly in the code. (This list passing is not that efficient, it could be made faster by using references.)

In contrast, in reduce, there no such return list. reduce only works on the input list of values and returns a single value.

like image 200
daxim Avatar answered Jan 01 '23 17:01

daxim