What is the best way to gunzip files with Perl?
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.6开发者_Python百科7 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())
"...
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 !
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.
And I also don't understand why
while (<$z>)
is slower thanwhile (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.
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.
精彩评论