• Home

  • Custom Ecommerce
  • Application Development
  • Database Consulting
  • Cloud Hosting
  • Systems Integration
  • Legacy Business Systems
  • Security & Compliance
  • GIS

  • Expertise

  • About Us
  • Our Team
  • Clients
  • Careers

  • Blog

  • EpiTrax

  • VisionPort

  • Contact
  • Our Blog

    Ongoing observations by End Point Dev people

    Benchmarking in Perl: Map versus For Loop

    Steph Skardal

    By Steph Skardal
    May 5, 2011

    Last week, I was coding in Perl for an Interchange project. I’ve been in and out of Perl and Ruby a lot lately. While I was working on the project, I came across the following bit of code and wanted to finally sit down and figure out how to use the map function in Perl on this bit of code.

    my @options;
    for my $obj (@$things) {
        push @options, {
            value => $obj->{a},
            label => $obj->{b}
        };        
    }
    return \@options;
    

    I’m a big fan of Ruby’s inject method and in general a fan of the Enumerable Module, but I have a brain block when it comes to using the map method in both Perl and Ruby. I spent a little time investigating and working on a small local Perl script to test the implementation of the map method. I came up with the following:

    return [ map {
        {
            value => $_->{a},
            label => $_->{b}
        }
    } @$things ];
    

    After that, I wanted to make sure the code change was justified. The Interchange application that is the source of this code is built for performance, so I wanted to ensure this change didn’t hinder performance. It’s been a while since I’ve done benchmarking in Perl, so I also had to refresh my memory regarding using the Benchmark module. I came up with:

    #!/usr/bin/perl
    
    use Benchmark;
    
    my $count = 1000000;
    my $things = [
        {'a' => 123, 'b' => 456, 'c' => 789 },
        {'a' => 456, 'b' => 789, 'c' => 123 }
    ];
    
    #Test definitions as methods to mimic use in application
    my $test1 = sub {
        my @options;
        for my $obj (@$things) {
            push @options, {
                value => $obj->{a},
                label => $obj->{b} 
            };
        }
        return \@options;
    };
    my $test2 = sub {
        return [ map {
            { 
                value => $_->{a},
                label => $_->{b}
            }
        } @$things ];
    };
    
    #Benchmark tests & results.
    $t0 = Benchmark->new;
    $test1->() for(1..$count);
    $t1 = Benchmark->new;
    $td = timediff($t1, $t0);
    print "the code for test 1 took:",timestr($td),"\n";
    
    $t0 = Benchmark->new;
    $test2->() for(1..$count);
    $t1 = Benchmark->new;
    $td = timediff($t1, $t0);
    
    print "the code for test 2 took:",timestr($td),"\n";
    

    The results were:

    Test # Before (For Loop) After (Map)
    1 5 sec 4 sec
    2 5 sec 4 sec
    3 5 sec 5 sec
    4 5 sec 5 sec
    5 6 sec 4 sec
    6 6 sec 4 sec
    7 6 sec 4 sec
    8 5 sec 5 sec
    9 5 sec 4 sec
    10 5 sec 4 sec
    Average 5.3 sec 4.3 sec

    In this case, replacing the imperative programming style here with Functional programming (via map) yielded a small performance improvement, but the script executed each method 1,000,000 times, so the performance gain yielded by just one method call is very small. I doubt it’s worth it go on a code cleanup rampage to update and test this, but it’s good to keep in mind moving forward as small bits of the code are touched. I also wonder if the performance will vary when the size of $things changes — something I didn’t test here. It was nice to practice using Perl’s map method and Benchmark module. Yippee.

    performance perl


    Comments