2

Is there a faster solution than my actual 'zcat' solution to gunzip files with Perl?

A little benchmark:

#!/usr/bin/perl

use strict;
use warnings;
use Benchmark qw(cmpthese timethese);
use IO::Uncompress::Gunzip qw(gunzip);

my $re = qr/test/;

my $bench = timethese($ARGV[1], {

  zcat => sub {
    if (defined open(my $FILE, "-|", "zcat " . $ARGV[0]))
    {
      while (<$FILE>)
      {
        print $_  if ($_ =~ $re);
      }
      close($FILE);
    }
  },

  io_gunzip => sub {
    my $z = new IO::Uncompress::Gunzip $ARGV[0];
    while (<$z>)
    {
      print $_  if ($_ =~ $re);
    }
  },

  io_gunzip_getline => sub {
    my $z = new IO::Uncompress::Gunzip $ARGV[0];
    while (my $line = $z->getline())
    {
      print $line if ($line =~ $re);
    }
  },

} );

cmpthese $bench;

1;

give me these results:

# zcat test.gz|wc -l
566
# zcat test2.gz|wc -l
60459

# ./zip_test.pl test.gz 500
Benchmark: timing 500 iterations of io_gunzip, io_gunzip_getline, zcat...
 io_gunzip:  4 wallclock secs ( 3.01 usr +  0.01 sys =  3.02 CPU) @ 165.56/s (n=500)
io_gunzip_getline:  3 wallclock secs ( 2.58 usr +  0.03 sys =  2.61 CPU) @ 191.57/s (n=500)
      zcat:  2 wallclock secs ( 0.20 usr  0.34 sys +  0.55 cusr  1.10 csys =  2.19 CPU) @ 228.31/s (n=500)
                   Rate         io_gunzip io_gunzip_getline              zcat
io_gunzip         166/s                --              -14%              -27%
io_gunzip_getline 192/s               16%                --              -16%
zcat              228/s               38%               19%                --

# ./zip_test.pl test2.gz 50
Benchmark: timing 50 iterations of io_gunzip, io_gunzip_getline, zcat...
 io_gunzip: 31 wallclock secs (29.67 usr +  0.11 sys = 29.78 CPU) @  1.68/s (n=50)
io_gunzip_getline: 26 wallclock secs (24.86 usr +  0.04 sys = 24.90 CPU) @  2.01/s (n=50)
      zcat:  5 wallclock secs ( 2.42 usr  0.19 sys +  1.19 cusr  0.27 csys =  4.07 CPU) @ 12.29/s (n=50)
                    Rate         io_gunzip io_gunzip_getline              zcat
io_gunzip         1.68/s                --              -16%              -86%
io_gunzip_getline 2.01/s               20%                --              -84%
zcat              12.3/s              632%              512%                --

And I also don't understand why "while (<$z>)" is slower than "while (my $line = $z->getline())"...

4

4 回答 4

4

I updated my benchmark with PerlIO::gzip as runrig suggested.

My updated benchmark:

#!/usr/bin/perl

use strict;
use warnings;
use Benchmark qw(cmpthese timethese);
use IO::Uncompress::Gunzip qw(gunzip);
use PerlIO::gzip;

my $re = qr/test/;

my $bench = timethese($ARGV[1], {

  zcat => sub {
    if (defined open(my $FILE, "-|", "zcat " . $ARGV[0]))
    {
      while (<$FILE>)
      {
        print $_  if ($_ =~ $re);
      }
      close($FILE);
    }
  },

  io_gunzip => sub {
    my $z = new IO::Uncompress::Gunzip $ARGV[0];
    while (<$z>)
    {
      print $_  if ($_ =~ $re);
    }
  },

  io_gunzip_getline => sub {
    my $z = new IO::Uncompress::Gunzip $ARGV[0];
    while (my $line = $z->getline())
    {
      print $line if ($line =~ $re);
    }
  },

  perlio_gzip => sub {
    if (defined open(my $FILE, "<:gzip", $ARGV[0]))
    {
      while (<$FILE>)
      {
        print $_  if ($_ =~ $re);
      }
      close($FILE);
    }
  },

} );

cmpthese $bench;

1;

New results:

# zcat test.gz| wc -l
566
# zcat test2.gz| wc -l
60459
# zcat test3.gz| wc -l
604590
# ./zip_test.pl test.gz 1000
Benchmark: timing 1000 iterations of io_gunzip, io_gunzip_getline, perlio_gzip, zcat...
 io_gunzip:  6 wallclock secs ( 6.07 usr +  0.03 sys =  6.10 CPU) @ 163.93/s (n=1000)
io_gunzip_getline:  6 wallclock secs ( 5.23 usr +  0.02 sys =  5.25 CPU) @ 190.48/s (n=1000)
perlio_gzip:  0 wallclock secs ( 0.62 usr +  0.01 sys =  0.63 CPU) @ 1587.30/s (n=1000)
      zcat:  6 wallclock secs ( 0.37 usr  0.98 sys +  0.94 cusr  2.86 csys =  5.15 CPU) @ 194.17/s (n=1000)
                    Rate    io_gunzip io_gunzip_getline         zcat perlio_gzip
io_gunzip          164/s           --              -14%         -16%        -90%
io_gunzip_getline  190/s          16%                --          -2%        -88%
zcat               194/s          18%                2%           --        -88%
perlio_gzip       1587/s         868%              733%         717%          --
# ./zip_test.pl test2.gz 50
Benchmark: timing 50 iterations of io_gunzip, io_gunzip_getline, perlio_gzip, zcat...
 io_gunzip: 30 wallclock secs (29.50 usr +  0.11 sys = 29.61 CPU) @  1.69/s (n=50)
io_gunzip_getline: 25 wallclock secs (24.85 usr +  0.10 sys = 24.95 CPU) @  2.00/s (n=50)
perlio_gzip:  4 wallclock secs ( 3.22 usr +  0.01 sys =  3.23 CPU) @ 15.48/s (n=50)
      zcat:  4 wallclock secs ( 2.35 usr  0.23 sys +  1.29 cusr  0.28 csys =  4.15 CPU) @ 12.05/s (n=50)
                    Rate    io_gunzip io_gunzip_getline         zcat perlio_gzip
io_gunzip         1.69/s           --              -16%         -86%        -89%
io_gunzip_getline 2.00/s          19%                --         -83%        -87%
zcat              12.0/s         613%              501%           --        -22%
perlio_gzip       15.5/s         817%              672%          28%          --
# ./zip_test.pl test3.gz 50
Benchmark: timing 50 iterations of io_gunzip, io_gunzip_getline, perlio_gzip, zcat...
 io_gunzip: 303 wallclock secs (299.28 usr +  1.30 sys = 300.58 CPU) @  0.17/s (n=50)
io_gunzip_getline: 250 wallclock secs (248.26 usr +  0.79 sys = 249.05 CPU) @  0.20/s (n=50)
perlio_gzip: 32 wallclock secs (32.03 usr +  0.20 sys = 32.23 CPU) @  1.55/s (n=50)
      zcat: 44 wallclock secs (24.64 usr  1.83 sys + 11.93 cusr  1.62 csys = 40.02 CPU) @  1.25/s (n=50)
                  s/iter    io_gunzip io_gunzip_getline         zcat perlio_gzip
io_gunzip           6.01           --              -17%         -87%        -89%
io_gunzip_getline   4.98          21%                --         -84%        -87%
zcat               0.800         651%              522%           --        -19%
perlio_gzip        0.645         833%              673%          24%          --

PerlIO::gzip is the fastest solution !

于 2009-10-08T17:22:16.967 回答
3

On typical desktop hardware, the zcat is all but certain to be I/O limited on non-trivial data (your sample files are awfully trivial, they'll be buffered for sure), in which case there isn't going to be any code-level optimization that will work for you. Spawning an external gzip seems perfect to me.

于 2009-10-07T17:59:19.337 回答
3

And I also don't understand why while (<$z>) is slower than while (my $line = $z->getline())...

Because $z is a self tied object, tied objects are notoriously slow, and <$z> uses the tied object interface to call getline() rather than directly calling the method.

Also you can try PerlIO-gzip but I suspect it won't be any/much faster than the other module.

于 2009-10-07T18:24:30.067 回答
2

The last time I tried it, spawning an external gunzip was considerably faster than using a Perl module (just like your benchmarks show). I suspect it's all the method calls involved in tying a filehandle.

I expect <$z> is slower than $z->getline for a similar reason. There's more magic involved in figuring out that the first needs to be translated into the second.

于 2009-10-07T18:20:59.153 回答