ファイルの先頭からその時点までを解凍するよりも効率的なことは何ができるのだろうかと思いました。答えはノーだと思われます。ただし、一部のCPU(Skylake)zcat | tail
では、CPUを最大クロック速度まで上げません。下記参照。カスタムデコーダーは、この問題を回避し、パイプ書き込みシステムコールを節約でき、おそらく10%速くなります。(または、電源管理設定を微調整しない場合、Skylakeでは最大60%高速です)。
カスタマイズされたzlibでできること skipbytes
関数をは、圧縮ブロック内のシンボルを解析して、実際に圧縮解除ブロックを再構築する作業を行わずに最後まで到達することです。これは、zlibの通常のデコード機能を呼び出して同じバッファーを上書きし、ファイル内を前方に移動するよりも大幅に高速(おそらく少なくとも2倍)になります。しかし、そのような関数を誰かが書いたかどうかはわかりません。(そして、特定のブロックでデコーダーが再起動できるようにファイルが特別に書かれていない限り、これは実際には機能しないと思います)。
それは次のようになりますので、私は、それらをデコードせずにデフレートのブロックをスキップする方法があった期待していたくらい速いです。ハフマンツリーは各ブロックの先頭に送信されるため、任意のブロックの先頭からデコードできます(と思います)。ああ、デコーダーの状態はハフマンツリーよりも大きいと思います。これは以前の32kiBのデコードされたデータでもあり、デフォルトではブロック境界を越えてリセット/忘れられません。同じバイトが繰り返し参照され続ける可能性があるため、巨大な圧縮ファイルでは文字通り一度だけ表示される場合があります。(たとえば、ログファイルでは、ホスト名は圧縮辞書で常に「ホット」のままであり、そのすべてのインスタンスは最初のインスタンスではなく前のインスタンスを参照します)。
zlib
マニュアルでは、あなたが使用する必要が言うZ_FULL_FLUSH
呼び出すときにdeflate
あなたが圧縮されたストリームは、その時点までシークできるようにする場合。それは「圧縮状態をリセットする」ので、それなしでは、後方参照は前のブロックに入ることができると思います。したがって、zipファイルがときどきフルフラッシュブロックで書き込まれない限り(すべての1Gまたは何かが圧縮にほとんど影響を与えないように)、私はあなたが当初よりもあなたが望むポイントまでより多くの作業を行わなければならないと思います考え。おそらく、どのブロックの先頭からも開始できないと思います。
これの残りは、あなたが望む最初のバイトを含むブロックの始まりを見つけて、そこからデコードすることだけが可能であると考えている間に書かれました。
しかし、残念なことに、Deflateブロックの開始は、圧縮ブロックの長さを示していません。非圧縮データは、先頭が16ビットサイズの非圧縮ブロックタイプでコーディングできますが、圧縮ブロックはそうではありません。RFC1951は、フォーマットを非常に読みやすく記述しています。動的なハフマンコーディングのブロックには、ブロックの前にツリーがあります(したがって、圧縮解除プログラムはストリーム内をシークする必要がありません)。
最大後方参照距離はわずか32kiBであるため、圧縮プログラムは非圧縮データを多くメモリに保持する必要はありませんが、ブロックサイズを制限することはありません。ブロックの長さは数メガバイトです。(これは、現在のブロックを解析せずに現在のブロックの終わりを見つけることができた場合、磁気ドライブでもディスクシークがメモリにシーケンシャルに読み込み、RAMのデータをスキップするのに十分な大きさです)。
zlibは可能な限り
ブロックを作成します。MarcAdlerによると、zlibはシンボルバッファがいっぱいになったときにのみ新しいブロックを開始します。デフォルト設定では、16,383シンボル(リテラルまたは一致)です。
出力をgzipで圧縮しましたseq
(これは非常に冗長であり、おそらく素晴らしいテストではありません)が、そのpv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c
上でDDR4-2666 RAMを搭載した3.9GHzのSkylake i7-6700kで〜62 MiB / sの圧縮データで実行されます。これは246MiB / sの圧縮解除されたデータです。これはmemcpy
、キャッシュに収まるには大きすぎるブロックサイズの場合、〜12 GiB / sの速度と比較して急激に変化します。
(の代わりにenergy_performance_preference
デフォルトに設定balance_power
するとbalance_performance
、Skylakeの内部CPUガバナーは、2.7GHz、〜43 MiB / sの圧縮データでのみ実行することを決定します。これsudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
を調整するために使用します。このような頻繁なシステムコールは、実際のCPUバインドのようには見えません電源管理ユニットに働きかけます。)
TL:DR:非常に遅いディスクzcat | tail -c
がない限り、高速CPUでもCPUバウンドです。 gzipは、実行したCPUの100%を使用し(およびによれば、クロックあたり1.81命令を実行しperf
)、tail
実行したCPUの0.162を使用しました(0.58 IPC)。それ以外の場合、システムはほとんどアイドル状態でした。
Linux 4.14.11-1-ARCHを使用しています。これは、デフォルトでKPTIが有効になっており、Meltdownを回避するため、これらのwrite
システムコールはすべて、gzip
以前よりも高価です:/
unzip
またはにシークが組み込まれているzcat
(ただし、通常のzlib
デコード機能を使用している)と、これらのパイプ書き込みがすべて保存され、Skylake CPUが最高のクロック速度で実行されます。(特定の種類の負荷に対するこのダウンクロックは、OSからCPU周波数の意思決定をオフロードするIntel Skylake以降に固有のものです。CPUが実行していることに関するデータが多く、より速く増減できるためです。通常は良好ですが、ここでは、より保守的なガバナー設定では、Skylakeが最高速度まで上昇しないことにつながります)。
システムコールはなく、必要な開始バイト位置に達するまでL2キャッシュに収まるバッファを書き換えるだけで、少なくとも数%の違いが生じる可能性があります。たぶん10%でも、ここで数字を作っているだけです。zlib
キャッシュフットプリントの大きさ、およびKPTIが有効になっているすべてのシステムコールでのTLBフラッシュ(およびuop-cacheフラッシュ)がどれだけ痛いかを確認するための詳細なプロファイルは作成していません。
シークインデックスをgzipファイル形式に追加するソフトウェアプロジェクトがいくつかあります。誰にもシーク可能な圧縮ファイルを生成してもらうことができない場合、これは役に立ちませんが、他の将来の読者が恩恵を受けるかもしれません。
インデックスはときにのみ動作するように設計しているので、おそらくどちらもこれらのプロジェクトの、インデックスなし圧縮ストリームをスキップする方法を知っているデコード機能を持たないで利用できます。