【问题标题】:What is the best way to gunzip files with Perl?用 Perl 压缩文件的最佳方法是什么?
【发布时间】:2009-10-07 17:30:06
【问题描述】:

有没有比我实际使用 Perl 压缩文件的“zcat”解决方案更快的解决方案?

一个小基准:

#!/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;

给我这些结果:

# 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%                --

我也不明白为什么“while (&lt;$z&gt;)”比“while (my $line = $z-&gt;getline())”慢...

【问题讨论】:

  • 查看 cpan.org - 可能有一个模块可以做到这一点。
  • @Paul: 喜欢 IO::Uncompress::Gunzip 吗?
  • 我在手机上写了那条评论,所以我没有办法查找并写下真正的答案,但听起来确实很有可能。
  • 您的问题远没有您的标题所表明的一般性。您希望保留原始文件未压缩并对整个未压缩内容进行操作(在这种情况下使用正则表达式)。这是一个比“使用 Perl 压缩文件的最佳方法是什么?”更具体的问题。

标签: performance perl gunzip


【解决方案1】:

我按照 runrig 的建议使用 PerlIO::gzip 更新了我的基准测试。

我更新的基准:

#!/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;

新结果:

# 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 是最快的解决方案!

【讨论】:

  • 不幸的是,PerlIO::gzip 没有打包在 Debian 中... :(
  • 这忽略了一个事实,即分叉的 zcat 可以(并且将,如果可用)使用第二个 CPU 内核,然后导致更低的挂钟时间。 Benchmark.pm 完全忽略了实际的挂钟时间,并根据 CPU 时间的总和计算 s/iter 和 Rate,包括子进程(可能并行运行),所以即使 zcat 在 2 个内核可用的情况下完成得更快,它也会声称 perlio_gzip 更快 m -( ...另外,解压缩 600K 可能非常快,以至于这里的初始开销太大了,也请尝试更大的文件。
  • 这篇文章已经有将近 7 年的历史了。 PerlIO::gzip 现在被打包在 Debian 中: $ sudo apt-get install libperlio-gzip-perl
  • 再一次,它仍然不在 RHEL 中(尽管您可以从 Fedora 获得它)。 :)
【解决方案2】:

在典型的桌面硬件上,zcat 几乎肯定会受到非平凡数据的 I/O 限制(您的示例文件非常简单,它们肯定会被缓冲),在这种情况下不会发生成为任何适合您的代码级优化。生成外部 gzip 对我来说似乎很完美。

【讨论】:

    【解决方案3】:

    我也不明白为什么while (&lt;$z&gt;)while (my $line = $z-&gt;getline()) 慢...

    因为$z是一个自绑定对象,所以绑定对象是出了名的慢,而&lt;$z&gt;使用绑定对象接口调用getline()而不是直接调用方法。

    您也可以尝试PerlIO-gzip,但我怀疑它不会比其他模块快任何/快得多。

    【讨论】:

    • +1 用于回答我的第二个问题,但我对第一个问题的答案更感兴趣... :)
    • 实际上 PerlIO::gzip 似乎是最快的……真的更快!我很快就会用我的新板凳更新我的问题!
    【解决方案4】:

    我上次尝试时,生成外部 gunzip 比使用 Perl 模块快得多(就像您的基准测试显示的那样)。我怀疑这是绑定文件句柄所涉及的所有方法调用。

    出于类似的原因,我预计 &lt;$z&gt;$z-&gt;getline 慢。弄清楚第一个需要翻译成第二个涉及到更多的魔法。

    【讨论】:

    • 如果你有多个核心,你就有效地分离了它们之间的工作。如果您使用库,则它们都在同一个核心上。
    猜你喜欢
    • 2014-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-07
    • 2017-08-30
    • 1970-01-01
    相关资源
    最近更新 更多