in reply to Challenge: Construct an unpack string

Given the form of the input and that the fields can overlap (assuming that was intentional), using unpack for this is awkward and inefficient.

You have to parse the input spec. from it's absolute pos/length pairs into the relative form (length/backspace) form required by unpack and build the string template. But the unpack has to parse the string template and convert that back into the position/offset pairs to perform the extractions.

Much simpler, (and with having benchmarked, I'll bet quicker), to use substr for this directly:


Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

Replies are listed 'Best First'.
Re^2: Challenge: Construct an unpack string
by atcroft (Abbot) on Sep 26, 2006 at 18:50 UTC

    Actually, if you agree that the following code would be valid for a benchmark, then you might be surprised.

    Code:

    #!/usr/bin/perl -w use strict; use Benchmark qw(:all); timethese( 1_000_000, { 'unpack' => sub { my $str = q{0123456789abcdefghijklmnopqrstuvwxyz}; my ( $name1, $name3, $name2 ) = unpack( q{a10 a14 X16 a3), + $str ); }, 'map_browseruk' => sub { my $str = q{0123456789abcdefghijklmnopqrstuvwxyz}; my @fields = ( [ name1 => { start => 0, len => 10 } ], [ name2 => { start => 8, len => 3 } ], [ name3 => { start => 11, len => 14 } ], ); my ( $name1, $name2, $name3 ) = map { substr $str, $_->[1] +{start}, $_->[1]{len} } @fields; }, 'unpack_setup' => sub { my $str = q{0123456789abcdefghijklmnopqrstuvwxyz}; my ( $name1, $name3, $name2 ); }, 'map_browseruk_setup' => sub { my $str = q{0123456789abcdefghijklmnopqrstuvwxyz}; my @fields = ( [ name1 => { start => 0, len => 10 } ], [ name2 => { start => 8, len => 3 } ], [ name3 => { start => 11, len => 14 } ], ); my ( $name1, $name2, $name3 ); }, 'map_jwkrahn' => sub { my $str = q{0123456789abcdefghijklmnopqrstuvwxyz}; my @fields = ( [ name1 => { start => 0, len => 10 } ], [ name2 => { start => 8, len => 3 } ], [ name3 => { start => 11, len => 14 } ], ); my $format = join q[ ], map sprintf( q[x%d a%d X%d], @{ $_-[1] }{qw[start len] +}, $_->[1]{start} + $_->[1]{len} ), @fields; my ( $name1, $name2, $name3 ) = unpack( $format, $str ); }, 'map_jwkrahn_setup' => sub { my $str = q{0123456789abcdefghijklmnopqrstuvwxyz}; my @fields = ( [ name1 => { start => 0, len => 10 } ], [ name2 => { start => 8, len => 3 } ], [ name3 => { start => 11, len => 14 } ], ); my $format = join q[ ], map sprintf( q[x%d a%d X%d], @{ $_-[1] }{qw[start len] +}, $_->[1]{start} + $_->[1]{len} ), @fields; my ( $name1, $name2, $name3 ); }, } );

    Results (1_000_000 iterations):

    Benchmark: timing 1000000 iterations of map_browseruk, map_browseruk_setup, map_jwkrahn, map_jwkrahn_setup, unpack, unpack_setup... map_browseruk: 16 wallclock secs (14.77 usr + 0.04 sys = 14.81 CPU) @ 67521.94/s (n=1000000) map_browseruk_setup: 9 wallclock secs ( 9.27 usr + 0.03 sys = 9.30 CPU) @ 107526.88/s (n=1000000) map_jwkrahn: 29 wallclock secs (23.17 usr + 0.04 sys = 23.21 CPU) @ 43084.88/s (n=1000000) map_jwkrahn_setup: 23 wallclock secs (20.23 usr + 0.03 sys = 20.26 CPU) @ 49358.34/s (n=1000000) unpack: 4 wallclock secs ( 2.43 usr + 0.00 sys = 2.43 CPU) @ 411522.63/s n=1000000) unpack_setup: 1 wallclock secs ( 0.44 usr + 0.00 sys = 0.44 CPU) @ 2272727.27/s (n=1000000)

    I must say I was surprised. I included the times for "setup"-that is, everything but the operation itself, on the idea that perhaps the setup might have taken longer for the one method than another. Thoughts....?

    Update (26-Sept-2006): Updated formatting of results.

    Update (26-Sept-2006): Fixed typo. (Thanks to BrowserUk for pointing it out.)

    Update (26-Sept-2006): Fixed 2nd typo. (Thanks to chargrill for pointing it out.)

      If you hardcode the template to unpack, of course it will be quicker--but when the input data changes, the template won't without programmer intervention--which kinda negates the value of the benchmark.

      Actually, your entire benchmark is pretty suspect and you are comparing a bunch of entirely different things:

      1. A hardcoded unpack.
      2. Two different implementations of the original challenge: parsing the data according to the contents of the input structure.
      3. Three other bit of random code that do nothing in particlar to do with the problem--your "setup" stuff.

      So, no. I wouldn't agree that this is a valid benchmark :) Sorry.

      Also, please use cmpthese for benchmarking, the output from timethese is all but unintelligable.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

      What the hell is Q?

      map sprintf( q[x%d a%d X%d], Q{ $_-[1] }{qw[start len] +}, $_->[1] .............................................^

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.