巨大な圧縮プレーンテキストファイルを部分的に抽出するには?

巨大な圧縮プレーンテキストファイルを部分的に抽出するには?

1.5GBサイズのzipファイルがあります。

その内容はとんでもない大きなプレーンテキストファイル(60GB)であり、現在ディスクにすべてを抽出するのに十分なスペースがなく、抽出したとしてもすべて抽出したくありません。

マイユースケースでは、一部を確認できれば十分です。

だから、ファイルの圧縮をストリームに解いてファイルの範囲にアクセスしたいと思います(通常のテキストファイルの頭と尾からアクセスできるように)。

メモリ(たとえば、32 GBの表示から始めて最大100 kbの抽出)または行(通常のテキスト行3700-3900を提供)を使用します。

これを達成する方法はありますか?

答え1

ファイル(最小ファイルの最初の項目)gzipを抽出できます。したがって、そのアーカイブに大きなファイルが1つしかない場合は、次のことができます。zipzip

gunzip < file.zip | tail -n +3000 | head -n 20

たとえば、行3000から始めて20行を抽出します。

または:

gunzip < file.zip | tail -c +3000 | head -c 20

バイトについても同様です(サポートされheadている実装を仮定-c)。

Unixy 方式でアーカイブのすべてのメンバーに対して、次の操作を行います。

bsdtar xOf file.zip file-to-extract | tail... | head...

head組み込み関数を使用して次のこともksh93できます。/opt/ast/bin$PATH

.... | head     -s 2999      -c 20
.... | head --skip=2999 --bytes=20

いずれにせよ、//抽出したいセクションにリンクされているgzipファイルのセクション全体を常に解凍し、ここから削除する必要があることに注意してください。圧縮アルゴリズムの仕組みによって異なります。bsdtarunzip

答え2

unzip -pとddを使用するソリューション(例:10kbおよび1000ブロックオフセット抽出):

$ unzip -p my.zip | dd ibs=1024 count=10 skip=1000 > /tmp/out

注:非常に大きなデータを使用したいわけではありません...

答え3

大きなzipファイルの生成を制御できる場合とgzipの組み合わせの使用を考えてみてはいかがでしょうかzless

これにより、ファイルをポケットベルとして使用でき、zlessファイルを抽出せずにファイルの内容を表示できます。

圧縮形式を変更できない場合は、明らかに動作しません。だとしたらzlessもっと便利だと思います。

答え4

ファイルの最初からこの時点まで解凍するよりも効率的なことができるのではないかと思いました。答えは「いいえ」のようです。ただし、一部のCPU(Skylake)では、zcat | tailCPUは最大クロック速度で向上しません。下記をご覧ください。カスタムデコーダは問題を防ぎ、パイプライン書き込みシステムコールを保存し、速度が10%速くなる可能性があります。 (または電源管理設定を調整しないと、Skylakeは60%高速です。)


関数を含むカスタムzlibで実行できる最善の方法skipbytesは、解凍されたブロックを実際に再構築することを行わずに、圧縮ブロックのシンボルを解析して最後まで到達することです。これは、同じバッファを上書きしてファイルから前方に移動するzlibの汎用デコード関数を呼び出すよりもはるかに高速です(おそらく2倍以上)。しかし、誰かがそのような関数を書いたかどうかはわかりません。 (デコーダが特定のブロックで再起動できるようにファイルを特別に書かない限り、これは実際には機能しないと思います。)

Deflateチャンクをデコードせずにスキップできる方法があればと思います。たくさん急いで。ハフマンツリーは各ブロックの先頭に送信されるため、すべてのブロックの先頭からデコードできます。ああ、私の考えでは、デコーダの状態はハフマンツリーだけでなく、以前の32kiBのデコードされたデータでもあり、基本的にブロック境界を超えてリセット/忘れ​​られません。同じバイトは繰り返し繰り返し参照できるため、大容量圧縮ファイルでは一度だけ表示できます。 (たとえば、ログファイルでは、ホスト名は圧縮辞書で常に「ホット」状態に保たれ、ホスト名の各インスタンスは最初のインスタンスではなく古いインスタンスを参照します。)

zlib手動圧縮ストリームをこの時点まで検索できるようにするには、呼び出しZ_FULL_FLUSH時にこのプロパティを使用する必要があることを示します。deflate「圧縮状態をリセットする」ので、これがなければ、逆参照が前のブロックに入ることができると思います。したがって、zipファイルが時々フルフラッシュチャンク(例えば、それぞれ1Gや圧縮への影響を無視することができる他のもの)で書かれていない限り、必要な点までより多くのデコードを実行する必要があると思います。もともと考えました。どのブロックの先頭からも開始できない場合があります。


残りは、必要な最初のバイトを含むブロックの先頭を見つけてそこからデコードできると思われるときに作成されます。

しかし、残念なことに、Deflateブロックの始まりはそれがどれくらいかかるかを示していません。、圧縮ブロックの場合。圧縮できないデータは、以前の16ビットバイトサイズの圧縮されていないブロックタイプを使用してエンコードできますが、圧縮ブロックは次のことを行いません。RFC 1951には、フォーマットの非常に読みやすい説明があります。。動的ハフマンコーディングを使用するブロックはブロックの前にツリーがあるため(圧縮解除器はストリームを見る必要はありません)、コンプレッサは書き込み前にブロック全体をメモリに保持する必要があります。

最大逆方向参照距離はわずか32kiBであるため、コンプレッサは圧縮されていないデータをメモリに大量に保持する必要はありませんが、これによりブロックサイズは制限されません。ブロックの長さは数メガバイトです。 (これは、現在のブロックの終わりを解析せずに見つけることができる場合は、メモリを順次読み込み、RAMのデータをスキップするよりも、磁気ドライブでもディスクシークを実行するのに十分な大きさです。)

zlib はできるだけ長くブロックを作成します。 マーク・アドラーによると, zlib はシンボリックバッファがいっぱいになったときだけ新しいブロックを開始します。デフォルトは16,383記号(リテラルまたは一致)です。


出力をgzipで圧縮しましたがseq(非常に冗長なので良いテストではないかもしれません)、pv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -cDDR4-2666 RAMデータが実行されている3.9GHzのSkylake i7-6700kで約62MiB / sに圧縮されました。データ圧縮解除速度は246MiB/sで、memcpyブロックサイズが大きすぎてキャッシュに収まらないため、約12GiB/sの速度で無視できる変化です。

(Skylakeの内部CPUガバナーは、代わりにenergy_performance_preferenceデフォルト値に設定した場合、約43MiB / sの圧縮データである2.7GHzでのみ実行することにしました。デバイス)。balance_powerbalance_performancesudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'

翻訳: 博士:zcat | tail -cあなたが持っていない限り、高速CPUでもCPUにバインドされています。非常にディスクが遅い。 gzipはCPUの100%(参照によると、クロックあたり1.81命令perf)を使用し、tail0.162 CPU(0.58 IPC)を使用しました。それ以外の場合、システムはほとんどの時間アイドル状態に保たれます。

私はLinux 4.14.11-1-ARCHを使用しています。KPTIはデフォルトで有効になっています。Meltdownの問題を修正して、すべてのwritesyscallがgzip以前よりも高価になるようにします。 :/


unzipまたは のための検索機能を組み込むzcat(しかし、まだ一般的なzlib復号機能を使用している)これらのパイプライン書き込みをすべて保存し、Skylake CPUを最大クロック速度で実行できるようにします。 (特定の負荷に対するこれらのダウンクロックはIntel Skylake以降に固有であり、CPUが実行する作業に関するより多くのデータがあり、周波数をより速くアップ/ダウンできるため、オペレーティングシステムからCPU周波数決定をオフロードしました。これは通常良いですが、ここでは次のように保守的なガバナー設定では、Skylakeは最高速度で実行されません。

システムコールなしで目的の開始バイト位置に達するまで、L2キャッシュに収まるバッファを書き換えると、おそらく少なくとも数%の違いが発生します。たぶん10%かもしれませんが、ここでは数字を作っただけです。zlibKPTIが有効になっている場合、キャッシュ集約方式とすべてのシステムコールのTLBフラッシュ(およびuopキャッシュフラッシュ)がどの程度影響を受けるかを詳細に分析しませんでした。


gzipファイル形式に検索インデックスを追加するいくつかのソフトウェアプロジェクトがあります。。他の人があなたのために見つけることができるzipファイルを生成することができない場合、これは役に立ちませんが、将来の他の読者が助けることができます。

おそらく、これらのプロジェクトのどれも、インデックスなしでDeflateストリームをスキップする方法を知ることができるデコード機能はありません。なぜなら、インデックスなしで動作するように設計されているからです。はい書くことができる。

関連情報