非常に厳しい CPU 制約下でも正常に動作しますか? Java におけるさまざまな圧縮アルゴリズムのパフォーマンス比較

非常に厳しい CPU 制約下でも正常に動作しますか? Java におけるさまざまな圧縮アルゴリズムのパフォーマンス比較

この記事では、一般的に使用されているいくつかの圧縮アルゴリズムのパフォーマンスを比較します。結果は、一部のアルゴリズムが極めて厳しい CPU 制約下でも適切に動作できることを示しています。

この記事で比較する計算は次のとおりです。

  • JDK GZIP - 圧縮率が高い低速のアルゴリズムであり、圧縮されたデータは長期使用に適しています。 JDK の java.util.zip.GZIPInputStream / GZIPOutputStream はこのアルゴリズムの実装です。
  • JDK deflate - これは JDK の別のアルゴリズムです (zip ファイルではこのアルゴリズムが使用されます)。 gzip と異なるのは、アルゴリズムの圧縮レベルを指定できる点です。これにより、圧縮時間と出力ファイル サイズのバランスをとることができます。使用可能なレベルは、0 (圧縮なし)、1 (高速圧縮) から 9 (低速圧縮) です。その実装は java.util.zip.DeflaterOutputStream/InflaterInputStream です。
  • LZ4 圧縮アルゴリズムの Java 実装 - これはこの記事で説明する最も高速な圧縮アルゴリズムですが、その圧縮結果は最も高速な deflate よりもわずかに劣ります。仕組みを理解したい場合は、この記事を読むことをお勧めします。フレンドリーな Apache 2.0 ライセンスの下でリリースされています。
  • Snappy – これは Google が開発した非常に人気のある圧縮アルゴリズムで、速度と圧縮率の点で比較的優れた圧縮アルゴリズムを提供することを目的としています。これはテストに使用した実装です。また、Apache 2.0 ライセンスの下でリリースされています。

圧縮試験

どのファイルがデータ圧縮テストに適していて、どのファイルがほとんどの Java 開発者のコ​​ンピューターに存在するかを把握するのに、しばらく時間がかかりました (このテストを実行するためだけに、数百メガバイトのファイルを使用しなければならない状況にはしたくありません)。最後に、ほとんどの人は JDK ドキュメントをローカルにインストールしておくべきだと思いました。そこで、javadoc ディレクトリ全体を 1 つのファイルにマージし、すべてのファイルを連結することにしました。これは tar コマンドで簡単に実行できますが、誰もが Linux ユーザーであるとは限らないため、このファイルを生成するプログラムを作成しました。

  1. 公共 クラスInputGenerator {
  2. プライベート 静的 最終的な文字列 JAVADOC_PATH = "your_path_to_JDK/docs" ;
  3. 公共 静的 最終ファイル FILE_PATH = new File( "your_output_file_path" );
  4.   
  5. 静的 
  6. {
  7. 試す{
  8. if ( !FILE_PATH.exists() )
  9. Javadocファイルを作成します。
  10. }キャッチ(IOException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14.   
  15. プライベート 静的  void makeJavadocFile()はIOExceptionをスローします{
  16. 試してください( OutputStream os = new BufferedOutputStream( new FileOutputStream( FILE_PATH ), 65536 ) )
  17. {
  18. appendDir(os、新しいファイル(JAVADOC_PATH));
  19. }
  20. System.out.println( "Javadoc ファイルが作成されました" );
  21. }
  22.   
  23. プライベート 静的  void appendDir(最終出力ストリーム os、最終ファイルルート )IOExceptionをスローします{
  24. for ( ファイル f : root.listFiles() )
  25. {
  26. if ( f.isDirectory() )
  27. 追加ディレクトリ(os, f);
  28. それ以外 
  29. Files.copy(f.toPath(), os);
  30. }
  31. }
  32. }

私のマシン上のファイル全体のサイズは 354,509,602 バイト (338 MB) です。

テスト

最初は、ファイル全体をメモリに読み込んでから圧縮することを考えました。しかし、そうすると、4G マシンでもヒープ メモリ領域が簡単に使い果たされてしまうことが判明しました。

そこで、オペレーティング システムのファイル キャッシュを使用することにしました。ここで使用するテスト フレームワークは JMH です。このファイルは、ウォームアップ フェーズ中にオペレーティング システムによってキャッシュにロードされます (ウォームアップ フェーズ中に 2 回圧縮されます)。コンテンツを ByteArrayOutputStream に圧縮します (これが最速の方法ではないことは承知していますが、さまざまなテストではより安定しており、圧縮されたデータをディスクに書き込むのに時間がかかりません)。そのため、この出力を保存するためのメモリ領域も必要になります。

以下はテストクラスの基本クラスです。すべてのテストは圧縮された出力ストリームの実装のみが異なるため、この基本テスト クラスを再利用して、StreamFactory 実装からストリームを生成することができます。

  1. @OutputTimeUnit (時間単位.ミリ秒)
  2. @State (スコープ.スレッド)
  3. @フォーク( 1 )
  4. @Warmup (反復回数 = 2 )
  5. @測定(反復回数 = 3 )
  6. @ベンチマークモード (Mode.SingleShotTime)
  7. 公共 クラスTestParent {
  8. 保護されたパス m_inputFile;
  9.   
  10. @設定 
  11. 公共  voidセットアップ()
  12. {
  13. m_inputFile = InputGenerator.FILE_PATH.toPath();
  14. }
  15.   
  16. インターフェースStreamFactory
  17. {
  18. パブリックOutputStream getStream(最終OutputStream 基になるストリーム )IOExceptionをスローします
  19. }
  20.   
  21. 公共  int baseBenchmark( final StreamFactory factory)IOExceptionをスローします
  22. {
  23. ByteArrayOutputStream bos をnew ByteArrayOutputStream(( int ) m_inputFile.toFile().length());実行します
  24. 出力ストリーム os = factory.getStream( bos ) )
  25. {
  26. Files.copy(m_inputFile, os);
  27. os.flush();
  28. bos.size()を返します
  29. }
  30. }
  31. }

これらのテスト ケースは非常に似ています (ソース コードは記事の最後にあります)。ここでは、JDK deflate のテスト クラスという 1 つの例のみを示します。

  1. 公共 クラスJdkDeflateTestは TestParentを拡張します{
  2. @Param ({ "1" "2" "3" "4" "5" "6" "7" "8" "9" })
  3. 公共 整数m_lvl;
  4.   
  5. @ベンチマーク 
  6. 公共  int deflate()IOExceptionをスローします
  7. {
  8. ベースベンチマークを返す(新しいStreamFactory() {
  9. @オーバーライド 
  10. パブリックOutputStream getStream(OutputStream 基になるStream) はIOExceptionをスローします{
  11. 最終的なDeflater deflater = new Deflater( m_lvl, true );
  12. 戻る 新しいDeflaterOutputStream( 基になるStream、 deflater、 512 );
  13. }
  14. });
  15. }
  16. }

テスト結果

出力ファイルサイズ

まず、出力ファイルのサイズを見てみましょう。

||実装||ファイル サイズ (バイト)|| ||GZIP||64,200,201|| ||Snappy (通常)||138,250,196|| ||Snappy (フレーム)|| 101,470,113|| ||LZ4 (高速)|| 98,316,501|| ||LZ4 (高) ||82,076,909|| ||Deflate (lvl=1) ||78,369,711|| ||Deflate (lvl=2) ||75,261,711|| ||Deflate (lvl=3) ||73,240,781|| ||Deflate (lvl=4) ||68,090,059|| ||Deflate (lvl=5) ||65,699,810|| ||Deflate (レベル=6) ||64,200,191|| ||デフレート (レベル=7) ||64,013,638|| ||デフレート (レベル=8) ||63,845,758|| ||デフレート (レベル=9) ||63,839,200||

ファイル サイズは大きく異なることがわかります (60 MB から 131 MB)。さまざまな圧縮方法にかかる時間を見てみましょう。

圧縮時間

||実装||圧縮時間 (ミリ秒)|| ||Snappy.framedOutput ||2264.700|| ||Snappy.normalOutput ||2201.120|| ||Lz4.testFastNative ||1056.326|| ||Lz4.testFastUnsafe ||1346.835|| ||Lz4.testFastSafe ||1917.929|| ||Lz4.testHighNative ||7489.958|| ||Lz4.testHighUnsafe ||10306.973|| ||Lz4.testHighSafe ||14413.622|| ||deflate (lvl=1) ||4522.644|| ||deflate (lvl=2) ||4726.477|| ||deflate (lvl=3) ||5081.934|| ||deflate (lvl=4) ||6739.450|| ||deflate (lvl=5) ||7896.572|| ||deflate (lvl=6) ||9783.701|| ||deflate (lvl=7) ||10731.761|| ||deflate (lvl=8) ||14760.361|| ||deflate (lvl=9) ||14878.364|| ||GZIP ||10351.887||

圧縮時間とファイル サイズを表にまとめて、アルゴリズムのスループットを計算し、どのような結論を導き出せるかを確認します。

スループットと効率

||実装||時間 (ミリ秒)||非圧縮ファイル サイズ||スループット (Mb/秒)||圧縮ファイル サイズ (Mb)|| ||Snappy.normalOutput ||2201.12 ||338 ||153.5581885586 ||131.8454742432|| ||Snappy.framedOutput ||2264.7 ||338 ||149.2471409017 ||96.7693328857|| ||Lz4.testFastNative ||1056.326 ||338 ||319.9769768045 ||93.7557220459|| ||Lz4.testFastSafe ||1917.929 ||338 ||176.2317583185 ||93.7557220459|| ||Lz4.testFastUnsafe ||1346.835 ||338 ||250.9587291688 ||93.7557220459|| ||Lz4.testHighNative ||7489.958 ||338 ||45.1270888301 ||78.2680511475|| ||Lz4.testHighSafe ||14413.622 ||338 ||23.4500391366 ||78.2680511475|| ||Lz4.testHighUnsafe ||10306.973 ||338 ||32.7933332124 ||78.2680511475|| ||deflate (レベル=1) ||4522.644 ||338 ||74.7350443679 ||74.7394561768|| ||deflate (レベル=2) ||4726.477 ||338 ||71.5120374012 ||71.7735290527|| ||deflate (レベル=3) ||5081.934 ||338 ||66.5101120951 ||69.8471069336|| ||deflate (レベル=4) ||6739.45 ||338 ||50.1524605124 ||64.9452209473|| ||デフレート (レベル=5) ||7896.572 ||338 ||42.8033835442 ||62.6564025879|| ||デフレート (レベル=6) ||9783.701 ||338 ||34.5472536415 ||61.2258911133|| ||デフレート (レベル=7) ||10731.761 ||338 ||31.4952969974 ||61.0446929932|| ||デフレート (レベル=8) ||14760.361 ||338 ||22.8991689295 ||60.8825683594|| ||deflate (lvl=9) ||14878.364 ||338 ||22.7175514727 ||60.8730316162|| ||GZIP ||10351.887 ||338 ||32.651051929 ||61.2258911133||

ご覧のとおり、実装のほとんどは非常に非効率的です。Xeon E5-2650 プロセッサでは、高レベルの deflate は約 23Mb/秒、GZIP でも 33Mb/秒に過ぎず、おそらく満足できるものではありません。一方、最も速いデフォールトアルゴリズムは約 75Mb/秒、Snappy は 150Mb/秒、LZ4 (高速、JNI 実装) は驚異的な 320Mb/秒に達します。

表から、現在 2 つの実装が不利であることがはっきりとわかります。Snappy は LZ4 (高速圧縮) よりも遅く、圧縮されたファイルは大きくなります。逆に、LZ4(高圧縮率)は、deflate レベル 1 ~ 4 よりも遅く、出力ファイルのサイズも deflate レベル 1 と比べて大幅に大きくなります。

したがって、「リアルタイム圧縮」が必要な場合は、LZ4 (高速) JNI 実装またはレベル 1 deflate のいずれかを選択することになります。もちろん、会社がサードパーティのライブラリの使用を許可していない場合は、deflate のみを使用できます。また、空き CPU リソースの量と圧縮データをどこに保存するかも考慮する必要があります。たとえば、圧縮されたデータを HDD に保存する場合、100 Mb/秒以上のパフォーマンスはまったく役に立ちません (ファイルが十分に大きいと仮定)。HDD の速度がボトルネックになります。同じファイルを SSD ハードドライブにエクスポートすると、LZ4 でも遅くなりすぎます。データをネットワークに送信する前に圧縮する場合は、LZ4 を選択するのが最適です。これは、deflate75Mb/秒の圧縮パフォーマンスが、ネットワークの 125Mb/秒のスループットに比べて非常に小さいためです (もちろん、ネットワーク トラフィックにもパケット ヘッダーがあることは承知していますが、それを考慮に入れても、ギャップは依然としてかなり大きいです)。

要約する

  • データ圧縮が非常に遅いと思われる場合は、約 320 Mb/秒の速度でテキストを圧縮できる LZ4 (高速) 実装を検討してください。このような圧縮速度は、ほとんどのアプリケーションでは感知できないはずです。
  • サードパーティのライブラリを使用できないという制限がある場合、または少しだけ優れた圧縮ソリューションが必要な場合は、エンコードとデコードに JDK deflate (lvl=1) の使用を検討できます。同じファイルを最大 75Mb/秒の速度で圧縮できます。

ソースコード

Java 圧縮テスト ソースコード

元の記事は以下から転送されました: Java におけるさまざまな圧縮アルゴリズムのパフォーマンス比較

<<:  顔認識に興味がありますか? JavaScriptで実装された顔検出方法

>>:  PaaS でフェイルオーバー アルゴリズムを作成する際に避けるべき 3 つの落とし穴

ブログ    

推薦する

...

北京、宜荘市の111の道路で初の自動運転試験を開始

本日、北京市は有人自動運転試験を正式に開始した。北京経済技術開発区は40平方キロメートルのエリアを自...

毎日のアルゴリズム: 文字の繰り返しのない最長の部分文字列

[[421075]]この記事はWeChatの公開アカウント「3分でフロントエンドを学ぶ」から転載した...

...

AIがデータ統合の状況をどう変えるのか

生成 AI は統合の状況を変えています。 チームの経済性、速度、プロジェクト構造、配信モデルについて...

AIとデジタルワークスペースがパンデミック後の世界のリモートワーカーをサポート

従業員にとってリモートワークが実現可能であることを示す証拠は豊富にあります。 Zoom や Micr...

Nokelockの「1+2」戦略は、スマートロックを商業利用の新時代へと導きます

5月15日、世界有数のIoTロック企業であるnokelockの製品発表会が北京金宇シェラトンホテルで...

Megvii Technologyがロボット協調ネットワーク頭脳「Hetu」をリリース、エコシステムの改善に20億元を投資

現在、モノのインターネットの将来の発展方向は非常に明確であり、それが AIoT です。 AIは頭脳で...

顔認証決済には注意しましょう。お金を盗まれる可能性があります

受動的な収集は防御が難しい一部の学校では、この技術を搭載したカメラを使用して、生徒の授業状況を監視し...

NLP モデルは人間の言語を理解できないのでしょうか? Microsoft AdaTestはエラーの検出効率が5倍向上

自然言語処理 (NLP) モデルは人間の言語を理解できず、テキストを反対の意味として解釈しますが、こ...

ウエストワールドがやってくる: ロボットは独自の言語を使ってコミュニケーションとコラボレーションを学ぶ

人工知能研究チームOpenAIが発表した最新の報告書は、ロボットが自ら作成した新しい言語を使って互い...

C# データ構造とアルゴリズムにおける線形テーブルの簡単な分析

C# データ構造とアルゴリズムの線形リストとは何ですか?まず、C# のデータ構造とアルゴリズムにおけ...

Google の研究者が発狂: AI に人格があると信じ、有給休暇を取得し、チャットログが恐ろしい

この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI)より許可を得て転載...

...

Python のデータ構造とアルゴリズム - 順序付きリストの維持と二分

[[402075]]序文Bisect は、リストをソートしたままリストに要素を挿入するアルゴリズムを...