この記事では、いくつかの一般的に使用される圧縮アルゴリズムのパフォーマンスを比較します。結果は、一部のアルゴリズムが非常に要求の厳しいCPU制約の下で適切に機能することを示しています。
記事の比較には次のものがあります。
JDK GZIP-これは、圧縮率が高い遅いアルゴリズムであり、圧縮データは長期使用に適しています。 java.util.zip.gzipinputStream / gzipoutputStream in JDKのこのアルゴリズムの実装です。
JDK DEFLATE-これはJDKの別のアルゴリズムです(このアルゴリズムはZIPファイルで使用されます)。 GZIPと違うのは、アルゴリズムの圧縮レベルを指定して、圧縮時間と出力ファイルサイズのバランスをとることができることです。オプションのレベルは0(圧縮されていません)、1(高速圧縮)から9(遅い圧縮)です。その実装はjava.util.zip.deflateroutputStream/inflaterinputStreamです。
LZ4圧縮アルゴリズムのJava実装 - これは、この記事で導入されたアルゴリズムの間で最も速い圧縮速度です。最速のデフレートと比較して、その圧縮結果はわずかに悪化しています。
Snappy-これはGoogleが開発した非常に人気のある圧縮アルゴリズムです。比較的良好な速度と圧縮比を持つ圧縮アルゴリズムを提供することを目指しています。
圧縮テスト
また、データ圧縮テストに適しており、ほとんどのJava開発者のコンピューターに存在するファイルを見つけるのに多くの時間がかかりました(このテストを実行するために数百メガバイトのファイルを持っていることを望んでいません)。最後に、ほとんどの人はJDKのドキュメントをローカルにインストールする必要があると思いました。したがって、Javadocディレクトリ全体を1つのファイルにマージすることにしました - すべてのファイルをスプライシングします。これはTARコマンドで簡単に実行できますが、全員がLinuxユーザーであるわけではないため、このファイルを生成するプログラムを作成しました。
public class inputgenerator {private static final string javadoc_path = "your_path_to_jdk/docs"; public static final final file_path = new file( "your_output_file_path"); static {try {if(!file_path.exists())makejavadocfile(); } catch(ioexception e){e.printstacktrace(); }} private static void makejavadocfile()throws ioexception {try(outputstream os = new fileoutputStream(file_path)、65536)){appendir(os、new file(javadoc_path)); } system.out.println( "Javadocファイル作成"); } private static void appenddir(final outputstream os、final file root)はioexception {file f:root.listfiles()){if(f.isdirectory())appendir(os、f); else files.copy(f.topath()、os); }}}私のマシン上のファイル全体のサイズは、354,509,602バイト(338MB)です。
テスト
最初は、ファイル全体をメモリに読んでから圧縮したかったのです。ただし、結果は、4Gマシンでさえヒープメモリスペースを簡単に使い果たすことができることを示しています。
そこで、オペレーティングシステムのファイルキャッシュを使用することにしました。ここで使用するテストフレームワークはJMHです。このファイルは、ウォームアップフェーズ中にオペレーティングシステムによってキャッシュにロードされます(ウォームアップフェーズで2回圧縮されます)。コンテンツをbytearrayoutputStreamストリームに圧縮します(これは最速の方法ではありませんが、各テストでは比較的安定しており、圧縮データをディスクに書き込むのに時間がかかりません)。したがって、この出力を保存するにはメモリスペースが必要です。
以下は、テストクラスのベースクラスです。すべてのテストは、圧縮出力ストリームの異なる実装でのみ異なるため、このテストベースクラスを再利用して、StreamFactoryの実装からストリームを生成するだけです。
@OutputTimeUnit(TimeUnit.MilliseConds)@State(scope.thread)@fork(1)@warmup(iterations = 2)@measurement(iterations = 3)@benchmarkmode(mode.singleshottime)public class testparent {Protected path m_inputfile; @setup public void setup(){m_inputfile = inputgenerator.file_path.topath(); } interface streamfactory {public outputStream getStream(final outputStreamの基礎となる)IoExceptionをスローします。 } public int baseBenchmark(final StreamFactory Factory)throws ioException {try(bytearrayoutputStream bos = new bytearrayoutputStream(((int)m_inputfile.tofile()。length()。 os.flush(); return bos.size(); }}}これらのテストケースは非常に似ています(記事の最後にソースコードは利用できます)。ここにリストされている例の1つの例 - JDKデフレートのテストクラス。
パブリッククラスJDKDEFLATETESTは、testParent {@param({"1"、 "2"、 "3"、 "4"、 "5" 6 "、" 7 "8"、 "9"})public int m_lvlを拡張します。 @benchmark public int deflate()throws ioexception {return basebenchmark(new streamfactory(){@override public outputStream getStream(outputstream butryingstream)throws ioException {final deflater deflater = new deflater(m_lvl、true); return new deflatututttream; }}テスト結果
出力ファイルのサイズ
まず、出力ファイルのサイズを見てみましょう。
||実装||ファイルサイズ(バイト)|||| GZIP || 64,200,201 ||||スナッピー(通常)|| 138,250,196 ||||スナッピー(フレーム)|| 101,470,113 |||||| LZ4(高速)|| 98,316,501 |||| LZ4(High)|| 82,076,909 ||| DEFLATE(LVL = 1)|| 78,369,711 ||| -| DEFLATE(LVL = 2)|| 75,261,711 ||| deflate(LVL = 3)|| 73,240,781 (LVL = 4)|| 68,090,059 || || deflate(lvl = 5)|| 65,699,810 |||| deflate(lvl = 6)|| 64,200,191 ||||| deflate(lvl = 7)|| 64,013,638 |||| ||| deflate(lvl = 8)|| 63,845,758 | || 63,839,200 |||
ファイルサイズは大きく異なることがわかります(60MBから131MB)。さまざまな圧縮方法にかかる時間を見てみましょう。
圧縮時間
||実装||圧縮Time(MS)|||| Snappy.FramedOutput || 2264.700 |||| Snappy.NormalOutput || 2201.120 |||| LZ4.TEST FASTNATIAN || 1056.326 |||| LZ4.TESTFASTUNSA Fe || 1346.835 |||| lz4.testfastsafe || 1917.929 |||| lz4.testhighnative || 7489.958 |||| lz4.testhighunsafe || 10306.973 |||||||||||||||| || 14413.622 |||| deflate(lvl = 1)|| 4522.644 |||| deflate(lvl = 2)|| 4726.477 |||| deflate(lvl = 3)|| 5081.934 ||| deflate(lvl = 4)|| 6739.450 ||| || 7896.572 |||| deflate(lvl = 6)|| 9783.701 |||| deflate(lvl = 7)|| 10731.761 |||| deflate(lvl = 8)|| 14760.361 ||| deflate(lvl = 9)|| 1488.364 || 10351.887 ||
次に、圧縮時間とファイルサイズをテーブルにマージして、アルゴリズムのスループットをカウントし、どのような結論を引き出すことができるかを確認します。
スループットと効率
||実装||時間(MS)||非圧縮ファイルサイズ||スループット(MB/秒)|||圧縮ファイルサイズ|||| snappy.normalOutput || 2201.12 || 338 || 153.5581885586 || 131.8454742432 |||||| || 149.2471409017 || 96.7693328857 ||||| lz4.testfastnative || 1056.326 || 338 || 319.9769768045 || 93.7557220459 |||||||| || 176.2317583185 || 93.7557220459 |||||| -lz4.testfastunsafe || 1346.835 || 338 || 250.9587291688 || 93.755722059 |||||||||||| || 45.12708888301 || 78.2680511475 ||||| LZ4.TESTHIGHSAFE || 14413.622 || 338 || 23.4500391366 || 78.2680511475 |||||||||||| || 32.79333332124 || 78.2680511475 |||| deflate(lvl = 1)|| 4522.644 || 338 || 74.7350443679 || 74.7394561768 |||||||||||| || 71.5120374012 || 71.7735290527 |||| deflate(lvl = 3)|| 5081.934 || 338 || 66.5101120951 || 69.8471069336 ||| deflate(lvl = 4)|| 4)|| || 50.1524605124 || 64.9452209473 ||||| deflate(lvl = 5)|| 7896.572 || 338 || 42.8033835442 || 62.6564025879 |||| |||||| | | | || 34.5472536415 || 61.2258911133 |||||| deflate(lvl = 7)|| 10731.761 || 338 || 31.4952969974 || 61.0466929932 ||||||||||| || 22.8991689295 || 60.8825683594 |||| deflate(lvl = 9)|| 14878.364 || 338 || 22.7175514727 || 32.651051929 || 61.2258911133 ||
ご覧のとおり、実装のほとんどは非常に非効率的です。XeonE5-2650プロセッサでは、高レベルのデフレートは約23MB/秒であり、GZIPでさえ33MB/秒でさえ、おそらく満足するのが難しいです。同時に、最速のdefalteアルゴリズムは約75MB/秒に達することができ、Snappyは150MB/秒、LZ4(高速、JNI実装)は信じられないほどの320MB/秒に達することができます!
テーブルからは、現在不利な実装が2つあることが明確にわかります。SnappyはLZ4(高速圧縮)よりも遅く、圧縮ファイルが大きいことです。それどころか、LZ4(高圧縮率)はレベル1から4までのデフレートよりも遅く、出力ファイルのサイズはレベル1からデフレートのサイズよりもはるかに大きくなります。
したがって、「リアルタイム圧縮」を実行する必要がある場合は、LZ4(高速)JNI実装またはレベル1デフレートで間違いなく選択します。もちろん、会社がサードパーティのライブラリを許可していない場合は、DEFLATEのみを使用できます。また、無料のCPUリソースがいくつあるか、圧縮データが保存される場所を包括的に検討する必要があります。たとえば、圧縮データをHDDに保存する場合、上記の100MB/sのパフォーマンスは役に立ちません(ファイルが十分に大きいと仮定します) - HDDの速度はボトルネックになります。同じファイルがSSDハードドライブに出力されている場合、LZ4でさえ前のLZ4が遅すぎます。最初にデータを圧縮してからネットワークに送信する場合は、DEFLATE75MB/sの圧縮性能は125MB/sのスループットと比較して本当に小さいため、LZ4を選択することをお勧めします(もちろん、ネットワークトラフィックにもBaotouがあることは知っていますが、たとえそれが含まれていても、ギャップは非常に有効です)。
要約します
データ圧縮が非常に遅いと思われる場合は、LZ4(高速)の実装を検討できます。これにより、約320MB/秒のテキスト圧縮速度を実現できます。ほとんどのアプリケーションでは、このような圧縮速度を認識してはなりません。
サードパーティライブラリを使用できない場合や、わずかに優れた圧縮ソリューションを使用することに限定されている場合は、エンコードとデコードにJDK DEFLATE(LVL = 1)を使用することを検討できます。同じファイルが同じファイルを75MB/sに圧縮できます。
ソースコード
Java圧縮テストソースコード