web-dev-qa-db-ja.com

gzip圧縮されたファイルのレコード数(行数)を取得するための最速かつ最も効率的な方法

7.6 GBのgzipファイルでレコードカウントを実行しようとしています。 zcatコマンドを使用したいくつかのアプローチを見つけました。

$ zcat T.csv.gz | wc -l
423668947

これは機能しますが、時間がかかりすぎます(カウントを取得するのに10分以上)。私はさらにいくつかのアプローチを試しました

$ sed -n '$=' T.csv.gz
28173811
$ Perl -lne 'END { print $. }' < T.csv.gz
28173811
$ awk 'END {print NR}' T.csv.gz
28173811

これら3つのコマンドはすべて非常に高速に実行されていますが、28173811のカウントが正しくありません。

最小限の時間でレコードカウントを実行するにはどうすればよいですか?

17
Rahul

あなたが言及するsedPerlおよびawkコマンドは正しいかもしれませんが、それらはすべてcompressedデータを読み取り、その中の改行文字をカウントします。これらの改行文字は、非圧縮データの改行文字とは関係ありません。

圧縮されていないデータの行数を数えるには、圧縮を解除する方法はありません。 zcatを使用したアプローチは正しいアプローチであり、データが非常に大きいため、解凍するのに時間がかかりますwill

gzip圧縮と解凍を扱うほとんどのユーティリティは、同じ共有ライブラリルーチンを使用してそうする可能性が最も高いでしょう。これを高速化する唯一の方法は、デフォルトのルーチンよりもいくらか高速なzlibルーチンの実装を見つけ、再構築することです。 zcatそれらを使用します。

29
Kusalananda

Unpigzを使用します。

Kusalanandaの答えは正しいです。あなたはその内容をスキャンするためにファイル全体を解凍する必要があります/bin/gunzipは、これをシングルコアで可能な限り高速に実行します。 Pigz は、複数のコアを使用できるgzipの並列実装です。

悲しいことに、通常のgzipファイルの解凍自体は並列化できませんが、pigzは、gunzipunpigzの改良版を提供し、別のスレッドで読み取り、書き込み、チェックサムなどの関連作業を行います。いくつかの簡単なベンチマークでは、unpigzは私のコアi5マシンのgunzipのほぼ2倍の速度です。

お好みのパッケージマネージャーでpigzをインストールし、unpigzの代わりにgunzipを使用するか、zcatの代わりにunpigz -cを使用します。したがって、コマンドは次のようになります。

$ unpigz -c T.csv.gz | wc -l

もちろん、これはボトルネックがディスクではなくCPUであることを前提としています。

21
marcelm

すべてのパイプラインの問題は、基本的に作業が2倍になることです。解凍がどれほど高速であっても、データは別のプロセスに送られる必要があります。

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]': $!";

古い 16 GB RAMを搭載した2010 MacBook Pro および古い 8 GB RAMを搭載したThinkPad T4 で13 MBのgzip圧縮ファイル(1.4 GBに解凍)で試してみました==すでにキャッシュにあるファイル。 Macでは、Perlスクリプトはパイプラインを使用するよりも大幅に高速でしたが(5秒vs 22秒)、ArchLinuxではunpigzに失敗しました。

$ time -p ./gzlc.pl spy.gz 
 1154737 
 real 4.49 
 user 4.47 
 sys 0.01 

versus

$時間-p unpigz -c spy.gz | wc -l 
 1154737 
実際の3.68 
 user 4.10 
 sys 1.46

そして

$ time -p zcat spy.gz | wc -l 
 1154737 
実際の6.41 
 user 6.08 
 sys 0.86

明らかに、unpigz -c file.gz | wc -lは、速度の面でどちらも勝者です。そして、その単純なコマンドラインは、プログラムの作成を確実に上回ります。

5
Sinan Ünür

クサラナンダの答えはほぼ正しい。行を数えるには、改行を検索する必要があります。ただし、ファイルを完全に解凍することなく改行を検索することは理論的には可能です。

gzipはDEFLATE圧縮を使用します。 DEFLATEは、LZ77とハフマンエンコーディングの組み合わせです。改行のハフマンシンボルノードだけを把握し、残りを無視する方法があるかもしれません。ほぼ確実に、L277を使用してエンコードされた改行を探し、バイトカウントを保持し、その他すべてを無視する方法があります。

したがって、IMHOはunpigzやzgrepよりも効率的なソリューションを考え出すことが理論的に可能です。それは確かに実用的ではないと言われています(誰かがすでにそれを行っていない限り)。

4
IAmBarry

zgrep-cフラグ、および$パラメータを使用して実行できます。

この場合、-cはコマンドに一致する行の数を出力するように指示し、正規表現$は行末に一致するため、すべての行またはファイルに一致します。

zgrep -c $ T.csv.gz 

@StéphaneChazelasによるコメント-zgrepzcatgrepに関するスクリプトにすぎず、zcat | wc -lの元の提案と同様のパフォーマンスを提供するはずです。

1
Yaron

ご覧のように、ほとんどの回答は、コンテキストスイッチの数とプロセス間IOを最適化しようとしています。その理由は、ここで簡単に最適化できるのはこれだけだからです。

現在の問題は、そのリソースの必要量が、解凍のリソースの必要量に対してほとんど無視できることです。これが、最適化によって実際には何も速くならない理由です。

それが本当に加速される可能性がある場合、それは修正されたun-gzip(つまり、解凍)アルゴリズムであり、解凍されたデータストリームの実際の生成を省略します。むしろ、圧縮されたのものから圧縮解除されたストリームの改行の数を計算するだけです。それは難しいでしょう、それはgzipのアルゴリズムの深い知識を必要とするでしょう [〜#〜] lzw [〜#〜]Huffman 圧縮アルゴリズムのいくつかの組み合わせ) 。アルゴリズムは、軽量化で解凍時間を大幅に最適化することを可能にしない可能性が非常に高く、改行数を知るだけで十分です。それが可能であるとしても、本質的には新しいgzip解凍ライブラリが開発されているはずです(それは知るまで存在しません)。

あなたの質問に対する現実的な答えは、いいえ、それを大幅に速くすることはできないということです。

存在する場合は、並列化されたgzip解凍を使用できます。解凍には複数のCPUコアを使用できます。存在しない場合は、比較的簡単に開発できます。

xzの場合、並列コンプレッサー(pxz)が存在します。