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


16

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が発生しています。

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


5
レコードの数を数える必要があるのはなぜですか?それらを処理する前にそれらを数えようとしている場合、それはファイルを2回解凍する必要があることを意味します。
アンドリューヘンレ

3
これを行う理由に関する詳細情報が役立ちます。それが継続的なものである場合-つまり、定期的に多数のファイルを圧縮し、後でレコードの数を知る必要がある場合-圧縮されているとカウントして、ファイル名に数字を埋め込むのはなぜですか?
-jamesqf

3
メカニカルディスクからの9.7GBファイルの読み取りは、本質的に低速です。ファイルをSSDに保存し、gunzip / zcatの実行速度を確認します。しかし、@ jamesqfが言うように、行数をファイル名またはtgzのファイルに保存すると、そのファイルの抽出がはるかに高速になります。
ChuckCottrill

2
この作業を避けられないのには、理論的には十分な理由があります。「解凍せずに」データのいくつかの有用なプロパティを決定できる圧縮形式は、定義上、圧縮形式ほどではありません:)
hobbs

回答:


28

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

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

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


11
それは重要なプログラミング演習ですが、実行可能です。全体のポイントは再構築しないことzcatです。の作業の重要な部分はzcat、実際の出力を生成することです。ただし、\n文字を数えるだけの場合は必要ありません。gzip圧縮は基本的に、一般的な長い文字列を短い文字列に置き換えることで機能します。したがって、を含むディクショナリ内の長い文字列のみに注意し、\nそれらの(重み付き)出現をカウントする必要があります。たとえば、英語の規則により.\n、一般的な16ビット文字列です。
–MSalters

19

unpigzを使用します。

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

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

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

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

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


4
私のpigzマニュアルページには、少なくともその目的のために特別に準備されたdeflateストリームがなければDecompressionは並列化できないと述べています。その結果、pizzは解凍に単一のスレッド(メインスレッド)を使用しますが、読み取り、書き込み、計算のチェック用に他の3つのスレッドを作成し、状況によっては解凍を高速化できます。それでも、あなたのような私はそれはのように速くよりも少なくとも二倍だ見つからgzipない場合は理由の並列処理、
ステファンChazelas

@StéphaneChazelas良い点!これは、減圧の軽度の失速を説明しています。この情報をよりよく反映するように投稿を編集しました。
-marcelm

5

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

Perlには、gzip圧縮されたストリームを直接読み取ることができるPerlIO :: 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 T400で 13 MBのgzip圧縮ファイル(1.4 GBに解凍)で試してみました。Macでは、Perlスクリプトはパイプラインを使用するよりも大幅に高速でした(5秒対22秒)が、ArchLinuxではunpigzで失われました。

$ time -p ./gzlc.pl spy.gz 
1154737
本当の4.49
ユーザー4.47
sys 0.01

$ time -p unpigz -c spy.gz | wc -l
1154737
実3.68
ユーザー4.10
sys 1.46

そして

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

明らかに、unpigz -c file.gz | wc -l速度の点で、ここでの使用が勝者です。そして、その単純なコマンドラインは、プログラムの作成を確実に打ち負かしますが、どんなに短くてもです。


1
減圧計算と比較して、2つのプロセス間でデータを移動するために必要なリソースを大幅に過大評価していると思います。さまざまなアプローチをベンチマークしてみてください;)
marcelm

2
私のx86_64 Linuxシステム(古いハードウェアでも)の@SinanÜnür gzip | wcは、perlスクリプトと同じ速度です。そしてpigz | wc倍の速さです。gzip出力を/ dev / nullに書き込むかパイプに入れるwcかに関係なく、perlが使用する「gzipライブラリ」はgzipコマンドラインツールよりも高速であると信じています。パイプに関する別のMac / Darwin固有の問題があるかもしれません。このperlバージョンがまったく競争力があることはまだ驚くべきことです。
-rudimeier

1
x86_64 Linuxのインストールでは、のパフォーマンスはのパフォーマンスよりも良くzcat、悪いようですunpigz。Linuxシステムでのパイプラインの速度がMacに比べてはるかに速いことに驚いています。同じプログラムがベアメタル上よりも同じMac上でCPUが制限されたLinux VM上でより高速に実行されていたのを見ていたはずですが、私はそれを期待していませんでした。
シナンÜnür17年

1
それは面白い; 私のシステム(Debian 8.8 amd64、クアッドコアi5)では、perlスクリプトはわずかに遅くなります... 109Gの.gzファイルは1.1Gのテキストに圧縮解除され、perlスクリプトでは一貫して5.4秒zcat | wc -l、5.5秒かかります。正直なところ、特にLinuxとMacOS Xの間で人々がここで報告しているバリエーションに驚いています!
-marcelm

Macで見ているものを一般化できるかどうかはわかりませんが、奇妙なことが起こっています。解凍された1.4 GBファイルでwc -lは、2.5秒かかります。gzcat compressed.gz > /dev/null2.7秒かかります。それでも、パイプラインは22秒かかります。GNUを試してみるとwc、解凍されたファイルでは0.5秒しかかかりませんが、パイプラインでは22秒かかります。GNUのzcat実行には2倍の時間がかかりますzcat compressed.gz > /dev/null。これは、Mavericks、古いCore 2 Duo CPU、16 GB RAM、Crucial MX100 SSDにあります。
シナンÜnür17年

4

Kusalanandaの答えはほとんど正しいです。行を数えるには、改行を検索する必要があります。ただし、ファイルを完全に解凍せずに改行を検索することは理論的には可能です。

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

だから私見は、理論的にunpigzやzgrepよりも効率的なソリューションを考え出すことが可能です。言われていることは確かに実用的ではありません(誰かがすでにそれをやっていなければ)。


7
このアイデアの主な問題は、DEFLATEで使用されるハフマンシンボルがLZ77圧縮後のビットシーケンスに対応するため、それらと非圧縮ファイルのU + 000A文字との間に単純な関係がない可能性があることです。たとえば、1つのハフマン記号が「。」の最後の5ビットを意味する場合があります。「\ n」の最初の3ビットが続き、別の記号は「\ n」の最後の5ビットと「T」の8ビットすべてを意味します。
-zwol

@zwolいいえ、DeflateアルゴリズムのLZ77部分は、ビットシーケンスではなくバイトシーケンスを圧縮します。en.wikipedia.org/wiki/DEFLATE#Duplicate_string_elimination
ロスリッジ

1
@RossRidgeええ、私はそれを知りませんでしたが、それは私が言ったことを無効にするとは思わない。ハフマンシンボルが、それは、それぞれが可変ビット数に拡張し、その参照の次の段落に基づいて、私には見える、彼らはバイトの全体数を生成する必要はありませんすることができます。
-zwol

1
@zwol確かに、ビットストリーム内で一致するハフマンコードビットシーケンスを検索する必要がありますが、この答えはそうではないことを示唆していません。この答えの問題は、どのハフマンコードが最終的に生成するか、または改行文字を増やすかを判断するのは簡単ではないことです。改行を生成するLZ77コードは、スライディングウィンドウが移動するにつれて絶えず変化しています。つまり、ハフマンコードも変化しています。改行のみに関心があるので、出力部分とスライドウィンドウの一部を除いて、展開アルゴリズム全体を実装する必要があります。
ロス・リッジ

1

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

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

zgrep -c $ T.csv.gz 

@StéphaneChazelasがコメントしたように-はただzgrepのスクリプトでzcatありgrep、元の提案と同様のパフォーマンスを提供するはずですzcat | wc -l


2
こんにちは答えをヤロンのおかげでもzgrepはzcatのように多くの時間として取っている私は考えていくつかの他のアプローチを見つける必要が
ラーフル

8
zgrep一般に、データを圧縮解除してに送るzcat(と同じgzip -dcq)を呼び出すスクリプトなgrepので、助けにはなりません。
ステファンシャゼル

1
@StéphaneChazelas-コメントをありがとう、それを反映するために私の答えを更新してください。
ヤロン

0

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

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

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

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

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

以下のためにXZ、並列圧縮機(PXZ)が存在します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.