在 gzip 压缩文件中获取记录数(行)的最快和最有效的方法

Rah*_*hul 17 shell gzip

我正在尝试对 7.6 GB 的 gzip 文件进行记录计数。我发现使用该zcat命令的方法很少。

$ zcat T.csv.gz | wc -l
423668947
Run Code Online (Sandbox Code Playgroud)

这有效,但需要太多时间(超过 10 分钟才能获得计数)。我尝试了更多的方法,比如

$ sed -n '$=' T.csv.gz
28173811
$ perl -lne 'END { print $. }' < T.csv.gz
28173811
$ awk 'END {print NR}' T.csv.gz
28173811
Run Code Online (Sandbox Code Playgroud)

所有这三个命令的执行速度都非常快,但给出的计数不正确,为 28173811。

如何在最短的时间内执行记录计数?

Kus*_*nda 29

您提到的sed,perlawk命令可能是正确的,但它们都读取压缩数据并计算其中的换行符。这些换行符与未压缩数据中的换行符无关。

要计算未压缩数据中的行数,无法解压缩它。你用的方法zcat是正确的做法,并且由于数据是如此之大,这需要时间来解压。

大多数处理gzip压缩和解压缩的实用程序很可能会使用相同的共享库例程来执行此操作。加速它的唯一方法是找到zlib比默认例程更快的例程的实现,并重建例如zcat使用这些例程。

  • 这将是一个非平凡的编程练习,但可行。重点是_不_重建`zcat`。`zcat` 工作的一个重要部分是生成实际输出。但是如果你只计算 `\n` 个字符,那是没有必要的。`gzip` 压缩本质上是通过用较短的字符串替换常见的长字符串来工作的。所以你只需要关心字典中包含 `\n` 的长字符串,并计算它们的(加权)出现次数。例如,由于英文规则,`.\n` 是一个常见的 16 位字符串。 (11认同)

mar*_*elm 21

使用 unpigz。

Kusalananda的答案是正确的,你需要解压缩是整个文件扫描其内容。/bin/gunzip在单个内核上尽可能快地执行此操作。Pigzgzip可以使用多个内核的并行实现。

可悲的是,正常的gzip文件解压缩本身不能并行,但pigz确实提供了一个改进版本gunzipunpigz即做相关的工作,如阅读,写作,并在一个单独的线程执行校验。在一些快速基准测试中,unpigz几乎是gunzip我的核心 i5 机器的两倍。

pigz使用您最喜欢的包管理器进行安装,并使用unpigz代替gunzipunpigz -c代替zcat。所以你的命令变成:

$ unpigz -c T.csv.gz | wc -l
Run Code Online (Sandbox Code Playgroud)

当然,所有这些都假设瓶颈是 CPU,而不是磁盘。

  • 我的 `pigz` 手册页指出 _Decompression 不能并行化,至少在没有为此目的专门准备的 deflate 流的情况下不能并行化。因此,pigz 使用单线程(主线程)进行解压,但会创建另外三个线程进行读、写和校验计算,在某些情况下可以加快解压速度_。尽管如此,和你一样,我发现它至少比 `gzip` 快两倍,如果不是因为并行性 (4认同)

Sin*_*nür 5

所有管道的问题在于您实际上是在加倍工作。不管解压多快,数据仍然需要穿梭到另一个进程中。

Perl 有PerlIO::gzip,它允许您直接读取 gzip 压缩的流。因此,即使它的解压速度可能与以下的解压速度不匹配,它也可能提供优势unpigz

#!/usr/bin/env perl

use strict;
use warnings;

use autouse Carp => 'croak';
use PerlIO::gzip;

@ARGV or croak "Need filename\n";

open my $in, '<:gzip', $ARGV[0]
    or croak "Failed to open '$ARGV[0]': $!";

1 while <$in>;

print "$.\n";

close $in or croak "Failed to close '$ARGV[0]': $!";
Run Code Online (Sandbox Code Playgroud)

我在具有 16 GB RAM的旧2010 MacBook Pro具有 8 GB RAM的旧ThinkPad T400 上尝试使用 13 MB gzip 压缩文件(解压缩为 1.4 GB),该文件已在缓存中。在 Mac 上,Perl 脚本比使用管道明显快(5 秒对 22 秒),但在 ArchLinux 上,它输给了 unpigz:

$ time -p ./gzlc.pl spy.gz 
1154737
真实 4.49
用户 4.47
系统 0.01

相对

$ time -p unpigz -c spy.gz | wc -l
1154737
真实 3.68
用户 4.10
系统 1.46

$ time -p zcat spy.gz | wc -l
1154737
真实 6.41
用户 6.08
系统 0.86

显然,unpigz -c file.gz | wc -l就速度而言,使用在这里是赢家。而且,这个简单的命令行肯定胜过编写程序,无论程序有多短。

  • @SinanÜnür 在我的 x86_64 Linux 系统(也是旧硬件)上`gzip | wc` 的速度与您的 perl 脚本相同。和`猪| wc` 的速度是原来的两倍。`gzip` 以相同的速度运行,无论我是将输出写入 /dev/null 还是管道到 `wc` 我相信 perl 使用的“gzip 库”比 gzip 命令行工具更快。也许还有另一个 Mac/Darwin 特定的管道问题。令人惊讶的是,这个 perl 版本完全具有竞争力。 (2认同)