Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Re: Map Vs Foreach

by Ratazong (Monsignor)
on Nov 26, 2009 at 06:44 UTC ( [id://809488]=note: print w/replies, xml ) Need Help??


in reply to Map Vs Foreach

You might also check map versus for. There for and map are compared, and there is even some benchmark-measurement-code provided ... which you can adapt to measure the foreach - performance.


HTH, Rata

Replies are listed 'Best First'.
Re^2: Map Vs Foreach
by perlCrazy (Monk) on Nov 26, 2009 at 09:35 UTC
    Thanks for reply. i did some benchmarking and found that map is slower than foreach.
    Also I was under impression that map is actually faster than foreach, but my result is different !
    #!/opt/gsperl-5.8.6_1/bin/perl use strict; use Benchmark; my ($start,$end,$diff); my @data = (1..20000000); # start timer $start = new Benchmark; my %dataseen; my @arr; foreach my $x (@data) { push @arr, ($x+2); } # end timer $end = new Benchmark; # calculate difference my $diff = timediff($end, $start); # report print "Time taken by foreach loop was ", timestr($diff, 'all'), " seco +nds\n"; my $start1 = new Benchmark; my @arr1; @arr1 = map { push @arr1, ($_+ 2) } @data; # end timer my $end1 = new Benchmark; # calculate difference my $diff1 = timediff($end1, $start1); # report print "Time taken by map block was ", timestr($diff1, 'all'), " second +s\n";
    and output of code :
    Time taken by foreach loop was 9 wallclock secs ( 8.22 usr 0.32 sys + 0.00 cusr 0.00 csys = 8.54 CPU) seconds
    Time taken by map block was 15 wallclock secs (14.88 usr 0.72 sys + 0.00 cusr 0.00 csys = 15.60 CPU) seconds
      For what its worth:
      I was unhappy about your benchmark, since the two types of code did not appear to be the same:
      @arr1 = map { push @arr1, ($_+ 2) } @data;
      in particular seemed suspect to me. Also the order of memory allocation can affect benchmarks. When I ran the supplied code the foreach loop worked fine (5 seconds on my machine) but the map gave Out of memory!.

      So I rewrote each part to be a subroutine, and tided the map so it looked like this:
      sub mapsub { my @arr; @arr = map { ($_ + 2) } @data; }
      I can't say if that was faster, because it still gives Out of memory! Must be something to do with a temporary list (5.10.1 on Windows). Reducing the size of @data by a factor of 10 gives map taking around twice as long, and each taking under a second.
        Some more benchmarking:
        my @data = (1..8000000); foreach my $x (@data) { push @arr, ($x+2); } @arr1 = map { $_+ 2 } @data; @arr2 = map { push @arr2, ($_+ 2) } @data;
        resulted in the following measurement-output:

        Time taken by foreach loop was 2 wallclock secs ( 2.27 usr 0.03 sys + 0.00 cusr 0.00 csys = 2.30 CPU) seconds
        Time taken by map block was 4 wallclock secs ( 3.39 usr 0.34 sys + 0.00 cusr 0.00 csys = 3.73 CPU) seconds
        Time taken by map block (with push) was 5 wallclock secs ( 4.63 usr 0.05 sys + 0.00 cusr 0.00 csys = 4.67 CPU) seconds


        best regards, Rata
        (a bit surprised about the performance-differences)(btw.: ActiveState perl 5.8.8)

        Update
        As Ikegami pointed out correctly (thanks!!), the line with @arr2 is wrong. Changing it to

        map { push @arr2, ($_+ 2) } @data;

        results in

        Time taken by map block (with push) was 3 wallclock secs ( 2.33 usr 0.00 sys + 0.00 cusr 0.00 csys = 2.33 CPU) seconds

        being similar fast as the foreach ...

      Well....
      my @arr1; @arr1 = map { push @arr1, ($_+ 2) } @data;
      is not the way to use map to generate a new list of @data with values of +2. here is the right way...
      my @dataPlus2 = map { my $bullshit = 'XYZ'; #see below $_ + 2 }@data;
      map{} returns the value of the last statement. I put this $bullshit statement in there to "throw you off"...it means absolutely nothing!

      Use the power of the language.

      my @dataPlus2 = map { $_ + 2 }@data;
      my @roots = map { sqrt($_) }@data;

      map{} is best used for a line or two translations.

      I wouldn't normally do it this way, this is just to show you that it is possible:

      foreach my $root (map { sqrt($_) }@data) { #do something with this square root... }
        I wouldn't normally do it this way, this is just to show you that it is possible:
        foreach my $root (map { sqrt($_) }@data) { #do something with this square root... }

        Possible, but, as you doubtless know, not desirable: This version makes two passes over the list—or, rather, one pass over the list, then one pass over its transformation—whereas

        foreach my $orig ( @data ) { my $root = sqrt $orig; ... do something with square root here ... }
        does the same thing in only one pass.

        I do take issue, though, with the statement that map is best for 1- or 2-line transformations. I think that's a readability opinion, and these are always subjective. It also might lead some into using map for a short transformation to which it's not suited. I think a less subjective guideline, and one that might be more useful when deciding which to use, is:

        • Use map if we want to take in a list and output a list, and there is a 1-to-1 correspondence between input and output, however complicated that correspondence is to implement. (I know that one can use the fact that the return value from the BLOCK in map is interpreted in list context to get around this—indeed, I've done it—but I think that, if you know that, then you're past the point where general slogans will give you any guidance. :-) )
        • Use foreach for any other transformation, no matter how simple.

Log In?
Username:
Password:

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

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

    No recent polls found