In diesem Artikel wird die Leistung mehrerer häufig verwendeter Komprimierungsalgorithmen verglichen. Die Ergebnisse zeigen, dass einige Algorithmen unter äußerst anspruchsvollen CPU -Einschränkungen immer noch ordnungsgemäß funktionieren.
Die Vergleiche im Artikel umfassen:
JDK GZIP - Dies ist ein langsamer Algorithmus mit hohem Kompressionsverhältnis, und die komprimierten Daten sind für den langfristigen Gebrauch geeignet. java.util.zip.gzipinputstream / gzipoutputStream in JDK ist die Implementierung dieses Algorithmus.
JDK Deflate - Dies ist ein weiterer Algorithmus in JDK (dieser Algorithmus wird in ZIP -Dateien verwendet). Was es von GZIP unterscheidet, ist, dass Sie die Komprimierungsstufe des Algorithmus angeben können, damit Sie die Komprimierungszeit und die Ausgabedateigröße ausgleichen können. Die optionalen Werte sind 0 (nicht komprimiert) und 1 (schnell komprimiert) bis 9 (langsam komprimiert). Seine Implementierung ist java.util.zip.deflateroutputStream/InflaterInputStream.
Die Java -Implementierung des LZ4 -Komprimierungsalgorithmus - Dies ist die schnellste Komprimierungsgeschwindigkeit unter den in diesem Artikel eingeführten Algorithmen. Im Vergleich zur schnellsten Deflate sind seine Kompressionsergebnisse etwas schlechter.
Snappy - Dies ist ein sehr beliebter Komprimierungsalgorithmus, der von Google entwickelt wurde. Ziel ist es, Komprimierungsalgorithmen mit relativ guter Geschwindigkeit und Kompressionsverhältnis bereitzustellen.
Kompressionstest
Ich habe auch viel Zeit gebraucht, um herauszufinden, welche Dateien für Datenkomprimierungstests geeignet sind und auf den meisten Computern der Java -Entwickler vorhanden sind (ich möchte nicht, dass Sie ein paar hundert Megabyte Dateien haben, um diesen Test auszuführen). Schließlich dachte ich, dass die meisten Leute die Dokumentation für JDK vor Ort installieren sollten. Daher habe ich mich entschlossen, das gesamte Javadoc -Verzeichnis in eine Datei zusammenzuführen - alle Dateien zu spleißen. Dies kann einfach mit dem TAR -Befehl erfolgen, aber nicht jeder ist ein Linux -Benutzer. Deshalb habe ich ein Programm geschrieben, um diese Datei zu generieren:
public class InputGenerator {private statische endgültige Zeichenfolge javadoc_path = "your_path_to_jdk/docs"; public statische endgültige Datei Datei_Path = neue Datei ("your_output_file_path"); static {try {if (! file_path.exists ()) makejavadocFile (); } catch (ioException e) {e.printstacktrace (); }} private static void makejavadocFile () löscht IOException {try (outputStream os = new bufferedOutputStream (neuer FileOutputStream (Datei_Path), 65536)) {AppendDir (OS, neue Datei (javadoc_path)) aus. } System.out.println ("Javadoc -Datei erstellt"); } private static void appendDir (endgültiges OS -OS, endgültiger Dateistamm) löst IOException {für (Datei f: root.listfiles ()) {if (f.isdirectory ()) appenddir (os, f) aus; sonst files.copy (f.topath (), os); }}}Die Größe der gesamten Datei auf meinem Computer beträgt 354.509.602 Bytes (338 MB).
prüfen
Zuerst wollte ich die gesamte Datei in den Speicher lesen und dann komprimieren. Die Ergebnisse zeigen jedoch, dass selbst 4G -Maschinen problemlos aus dem Haufen Speicherplatz ausgehen können.
Deshalb habe ich beschlossen, den Datei -Cache des Betriebssystems zu verwenden. Das Testframework, das wir hier verwenden, ist JMH. Diese Datei wird während der Aufwärmphase vom Betriebssystem in Cache geladen (sie wird in der Aufwärmphase zweimal komprimiert). Ich werde den Inhalt in den BytearrayoutputStream -Stream komprimieren (ich weiß, dass dies nicht der schnellste Weg ist, aber für jeden Test relativ stabil ist und keine Zeit braucht, um die komprimierten Daten auf die Festplatte zu schreiben), sodass ein Speicherplatz benötigt wird, um diese Ausgabe zu speichern.
Unten finden Sie die Basisklasse der Testklasse. Alle Tests unterscheiden sich nur in den verschiedenen Implementierungen des komprimierten Ausgangsstroms, sodass Sie diese Testbasisklasse wiederverwenden und einfach einen Stream aus der StreamFactory -Implementierung erstellen können:
@OutputTimeUnit (TimeUnit.Milliseconds) @State (scope.thread) @Fork (1) @WarmUp (iterations = 2) @Measurement (iterations = 3) @BenchmarkMode (modus @Setup public void setup () {m_inputFile = InputGenerator.file_path.topath (); } interface streamFactory {public outputStream getStream (endgültiger Ausgangsstream unterliest) löst IOException aus; } public int baseBenchmark (endgültige StreamFactory Factory) löst ioException {try (bytearrayoutputStream bos = neuer bytearrayoutputStream ((int) m_inputFile.tofile (). Länge ()); outputStream os = factory.getstream (bos)) {Dateien.Copy (M_Inputfile, OS); os.flush (); return bos.size (); }}}Diese Testfälle sind sehr ähnlich (ihr Quellcode ist am Ende des Artikels verfügbar), und hier ist nur ein Beispiel aufgeführt - die Testklasse von JDK Deflate;
public class jdkdeflatetest erweitert testparent {@param ({"1", "2", "3", "4", "5", "6", "7", "8", "9"}) public int m_lvl; @Benchmark public int Deflate () löst IOException {return baseBenchmark (new StreamFactory () {@Override public outputStream (outputStream unterliest) aus. }}Testergebnisse
Die Größe der Ausgabedatei
Schauen wir uns zunächst die Größe der Ausgabedatei an:
|| Implementierung || Dateigröße (Bytes) |||| GZIP || 64.200,201 |||| Snappy (normal) || 138,250,196 |||| Snappy (gerahmt) || 101.470,113 ||||| lz4 (schnell) || 98,316,501 |||| lz4 (hoch) || 82,076,909 ||| Deflat (lvl = 1) || 78,369,711 |||| (LVL = 4) || 68.090.059 || || Deflate (lvl = 5) || 65,699,810 ||| ||| Deflat (LVL = 6) || 64,200,191 ||||| Deflat (lvl = 7) || 64,013,638 ||| | || || || || 63,845,85,7588 || zu 8) || 63,845, 845, 845, 845,85,758 || zu || 63.839.200 |||
Es ist ersichtlich, dass die Dateigröße stark variiert (von 60 MB bis 131 MB). Schauen wir uns an, wie lange es für verschiedene Komprimierungsmethoden dauert.
Druckzeit
|| Implementierung || Komprimierung Zeit (MS) |||| Snappy.FramedOutput || 2264.700 |||| Snappy.Normaloutput || 2201.120 ||| || lz4.Testfastnative || 1056.326 ||| Fe || 1346.835 |||| lz4.TestfastSafe || 1917.929 |||| || 14413.622 |||| Deflat (lvl = 1) || 4522.644 |||| Deflat (lvl = 2) || 4726.477 |||| || 7896.572 |||| Deflat (lvl = 6) || 9783.701 |||| Deflate (lvl = 7) || 10731.761 |||| || 10351.887 ||
Wir fusionieren dann die Komprimierungszeit und die Dateigröße in eine Tabelle, um den Durchsatz des Algorithmus zu zählen und zu sehen, welche Schlussfolgerungen gezogen werden können.
Durchsatz und Effizienz
|| Implementierung || Zeit (ms) || unkomprimierte Dateigröße || Durchsatz (mb/s) ||| Komprimierte Dateigröße (MB) |||| Snappy.Normaloutput || 2201.12 || 338 || 153.5581885586 || 131.8454742432222222222222222222222222222222222222222222222222222222222222222222432322223222222222222222222222222222222222222222222222222222222222222242232232232232232232232232232. || 149.2471409017 || 96.7693328857 ||| || 176.2317583185 || 93.7557220459 |||| || 45.1270888301 || 78.2680511475 |||| || 32.7933332124 || 78.2680511475 |||| Deflate (lvl = 1) || 4522.644 || 338 || 74.7350443679 || 74.7394561768 ||| zu Deklate (lvl = 2) | || 71.5120374012 || 71.7735290527 |||| Deflate (lvl = 3) || 5081.934 || 338 || 66.5101120951 || 69.8471069366 ||| zu Deflate (LVL = 4) | || 50.1524605124 || 64.9452209473 |||| Deflate (LVL = 5) || 7896.572 || 338 || 42.80333835442 || 62.6564025879 ||| zu Deflate (lvl = 6) | || 34.5472536415 || 61.2258911133 |||| | Deflate (LVL = 7) || 10731.761 || 338 || 31.495296974 || 61.044692932 || || 22.8991689295 || 60.8825683594 |||| Deflate (lvl = 9) || 14878.364 || 338 || 22.71755514727 || 60.873031616222222121212121211051051.87.8730351051051051051.887 || || 61.2258911133 ||
Wie zu sehen ist, sind die meisten Implementierungen sehr ineffizient: Bei Xeon E5-2650-Prozessoren beträgt die Ablagerung auf hoher Ebene etwa 23 MB/s, und selbst GZIP beträgt nur 33 MB/s, was wahrscheinlich schwer zu erfüllen ist. Gleichzeitig kann der schnellste Defalte -Algorithmus etwa 75 MB/s erreichen, Snappy beträgt 150 MB/s, und LZ4 (schnell, JNI -Implementierung) kann ein unglaubliches 320 MB/s erreichen!
Aus der Tabelle ist deutlich zu erkennen, dass derzeit zwei Implementierungen im Nachteil sind: Snappy ist langsamer als LZ4 (schnelle Komprimierung) und die komprimierte Datei ist größer. Im Gegenteil, LZ4 (hohes Kompressionsverhältnis) ist langsamer als die Entleerung von Stufen 1 bis 4, und die Größe der Ausgabedatei ist viel größer als die von Deflate von Level 1.
Wenn Sie also "Echtzeitkomprimierung" durchführen müssen, werde ich definitiv in der JNI-Implementierung von LZ4 (schnell) oder der Deflate Level 1 wählen. Wenn Ihr Unternehmen keine Bibliotheken von Drittanbietern zulässt, können Sie natürlich nur Deflate verwenden. Sie müssen auch umfassend überlegen, wie viele kostenlose CPU -Ressourcen vorhanden sind und wo die komprimierten Daten gespeichert werden. Wenn Sie beispielsweise komprimierte Daten auf HDD speichern möchten, ist die oben genannte 100 MB/s -Leistung für Sie nicht hilfreich (vorausgesetzt, Ihre Datei ist groß genug) - die Geschwindigkeit von HDD wird zum Engpass. Wenn dieselbe Datei auf die SSD -Festplatte ausgegeben wird, erscheint sogar der LZ4 zu langsam vor ihm. Wenn Sie zuerst die Daten komprimieren und an das Netzwerk senden möchten, wählen Sie LZ4 am besten, da die Komprimierungsleistung von Deflate75MB/s im Vergleich zum Durchsatz von 125 MB/s wirklich klein ist (natürlich weiß ich, dass auch ein Baotou im Netzwerkverkehr vorhanden ist, aber selbst wenn dies eingeschlossen ist, ist die Lücke ziemlich beträchtlich).
Zusammenfassen
Wenn Sie der Meinung sind, dass die Datenkomprimierung sehr langsam ist, können Sie die LZ4 -Implementierung (schnell) in Betracht ziehen, die Textkomprimierungsgeschwindigkeiten von etwa 320 MB/s erreichen kann - eine solche Komprimierungsgeschwindigkeit sollte für die meisten Anwendungen nicht wahrgenommen werden.
Wenn Sie sich darauf beschränken, keine Bibliotheken von Drittanbietern zu verwenden oder nur eine etwas bessere Komprimierungslösung zu haben, können Sie die Verwendung von JDK Deflate (LVL = 1) zum Codieren und Dekodieren in Betracht ziehen - dieselbe Datei kann dieselbe Datei auf 75 MB/s komprimieren.
Quellcode
Java -Komprimierungstest Quellcode