В этой статье будет сравниваться производительность нескольких часто используемых алгоритмов сжатия. Результаты показывают, что некоторые алгоритмы все еще работают должным образом под чрезвычайно требовательными ограничениями процессора.
Сравнения в статье включают:
JDK GZIP - это медленный алгоритм с высоким соотношением сжатия, и сжатые данные подходят для долгосрочного использования. java.util.zip.gzipinputstream / gzipoutputstream в JDK - это реализация этого алгоритма.
JDK Deflate - это еще один алгоритм в JDK (этот алгоритм используется в файлах ZIP). Что отличает его от GZIP, так это то, что вы можете указать уровень сжатия алгоритма, чтобы вы могли сбалансировать время сжатия и размер выходного файла. Необязательные уровни составляют 0 (не сжатый) и 1 (быстро сжатый) до 9 (медленно сжатый). Его реализация - java.util.zip.deflateroutputstream/fulaterinptstream.
Реализация Java алгоритма сжатия LZ4 - это самая быстрая скорость сжатия среди алгоритмов, представленных в этой статье. По сравнению с самым быстрым дефлятом результаты сжатия немного хуже.
Snappy - Это очень популярный алгоритм сжатия, разработанный Google. Он направлен на обеспечение алгоритмов сжатия с относительно хорошей скоростью и соотношением сжатия.
Тест на сжатие
Мне также потребовалось много времени, чтобы выяснить, какие файлы подходят для тестирования сжатия данных, и существуют на компьютерах большинства разработчиков Java (я не хочу, чтобы у вас было несколько сотен мегабайт файлов для запуска этого теста). Наконец, я думал, что большинство людей должны иметь документацию для JDK на местном уровне. Поэтому я решил объединить весь каталог Javadoc в один файл - сплайсируя все файлы. Это можно легко сделать с помощью команды TAR, но не все являются пользователем Linux, поэтому я написал программу для создания этого файла:
открытый класс inputGenerator {частная статическая конечная строка javadoc_path = "your_path_to_jdk/docs"; public static final file file_path = new File ("your_output_file_path"); static {try {if (! file_path.exists ()) makejavadocfile (); } catch (ioException e) {e.printstackTrace (); }} private static void makejavadocfile () бросает ioException {try (outputStream OS = new BufferedOutputStream (new FileOutputStream (file_path), 65536)) {appendDir (OS, новый файл (javadoc_path)); } System.out.println ("файл Javadoc создал"); } private static void appenddir (final outputStream OS, Final File Root) Throws ioException {for (file f: root.listfiles ()) {if (f.isdirectory ()) appenddir (os, f); else files.copy (f.topath (), os); }}}Размер всего файла на моей машине составляет 354 509 602 байт (338 МБ).
тест
Сначала я хотел прочитать весь файл в память, а затем сжать его. Тем не менее, результаты показывают, что даже 4G -машины могут легко исчерпывать пространство памяти.
Поэтому я решил использовать кэш файла операционной системы. Тестовая структура, которую мы используем здесь, это JMH. Этот файл будет загружен в кэш операционной системой во время фазы прогрева (он будет сжиматься дважды в фазе прогрева). Я сжимаю контент в поток BytearrayOutputStream (я знаю, что это не самый быстрый способ, но он относительно стабилен для каждого теста и не требует времени, чтобы записать сжатые данные на диск), поэтому для хранения этого выхода необходимо некоторое пространство памяти.
Ниже приведен базовый класс тестового класса. Все тесты различны только в различных реализациях сжатого выходного потока, поэтому вы можете повторно использовать этот тестовый базовый класс и просто генерировать поток из реализации StreamFactory:
@OutputTimeUnit (timeUnit.milliseconds) @state (scope.thread) @fork (1) @warmup (итерации = 2) @measurement (итерации = 3) @benchmarkmode (mode.singleshottime) public class testparent {защищенный путь m_inputfile; @Setup public void setup () {m_inputfile = inputGenerator.file_path.topath (); } интерфейс streamfactory {public outputStream getStream (окончательный outputStream MapeningStream) бросает ioException; } public int basebenchmark (final -fuctory factory) бросает ioexception {try (bytearrayoutputstream bos = new Bytearrayoutputstream ((int) m_inputfile.tofile (). Length ()); outputStream Os = factory.getStream (bos)) {files.copy (m_InpuTfile, os); os.flush (); return bos.size (); }}}Эти тестовые примеры очень похожи (их исходный код доступен в конце статьи), и здесь указан только один пример - тестовый класс JDK Deflate;
открытый класс jdkdeflatetest extends testparent {@param ({"1", "2", "3", "4", "5", "6", "7", "8", "9"}) public int m_lvl; @Benchmark public int dellate () бросает ioexception {return basebenchmark (new Streamfactory () {@Override public outputStream getStream (outputStream introningStream) Throws IoException {final Defflater Defflater = New Defflater (M_LVL, true); вернуть новый DefflatePuteam (nateLyingStream, Deflater, 512); }}Результаты теста
Размер выходного файла
Во -первых, давайте посмотрим на размер выходного файла:
|| Реализация || Размер файла (байты) |||| Gzip || 64,200,201 |||| 101,470,113 ||||||| (Fast) || 98,316,501 |||| LZ4 (HIGH) || 82,076,909 ||| Дефлятный (LVL = 1) || 78,369,711 |||| ДЕВЕРСИТЕЛЬ (LVL = 2) || 75,261,711 |||| (LVL = 3) || 73,240,781 ||||| || 68,090,059 || || Дефлят (lvl = 5) || 65,699,810 |||| Дефлят (lvl = 6) || 64,200,191 |||| (lvl = 7) || 64,013,638 |||| (LVL = 8) || 63,845,758 ||| (LVL = 8) || 63,845,758 ||| || 63,839,200 |||
Видно, что размер файла сильно варьируется (от 60 МБ до 131 МБ). Давайте посмотрим, сколько времени требуется для различных методов сжатия.
Время сжатия
|| Реализация || сжатие Time (ms) |||| fe || 1346.835 |||| lz4.testfastsafe || 1917.929 |||| lz4.testhighnative || 7489.958 |||| lz4.testhighunsafe || 10306.973 ||| HAS4.TestHighsafe || 14413.622 |||| Дефлят (lvl = 1) || 4522.644 |||| Дефлят (lvl = 2) || 4726.477 |||| Дефлят (lvl = 3) || 5081.934 ||| (lvl = 4) || 6739,450 ||| (lvl = 4) || 6739.450 ||| || 7896.572 |||| Дефлят (lvl = 6) || 9783,701 |||| Дефлят (lvl = 7) || 10731.761 |||| Дефлят (lvl = 8) || 14760,361 ||| || 10351.887 ||
Затем мы объединяем время сжатия и размер файла в таблицу, чтобы подсчитать пропускная способность алгоритма и посмотреть, какие выводы можно сделать.
Пропускная способность и эффективность
|| Реализация || Time (MS) || Размер несущественного файла || Пропускная продукция (MB/Sec) ||| Размер сжатого файла (MB) |||| || 149.2471409017 || 96.7693328857 |||| HOLS4.TestFastnative || 1056.326 || 338 || 319,9769768045 || 93,7557220459 ||||| || 176.2317583185 || 93.7557220459 ||||| HAL4.TestFastUnsafe || 1346.835 || 338 || 250,9587291688 || 93,7557220459 |||| HUS4.Testhighnative || 74995888888888888 88888 || 45.1270888301 || 78.2680511475 |||| lz4.testhighsafe || 14413.622 || 338 || 23.4500391366 || 78.2680511475 |||| || 32.7933332124 || 78.2680511475 |||| Дефлятный (lvl = 1) || 4522.644 || 338 || 74,7350443679 || 74,7394561768 ||| (LVL = 2) || 4726,477768 || 71.5120374012 || 71.7735290527 ||| Если бы дефлят (LVL = 3) || 5081.934 || 338 || 66,5101120951 || 69,8471069336 ||| (LVL = 4) || 6739,45 || 50.1524605124 || 64,9452209473 |||| Дефлятный (lvl = 5) || 7896,572 || 338 || 42,8033835442 || 62,6564025879 ||| deflate (LVL = 6) || 9783,701 || || 34.5472536415 || 61.2258911133 |||| Если бы дефлят (LVL = 7) || 10731.761 || 338 || 31,4952969974 || 61,0446929932 ||| DEFLATE (LVL = 8) || 14760,360.360.360.386111111113611386113861113861113861138611386113861138611386113861138611386113861138613611386113861138613861138613611386113861361361138 || 22,8991689295 || 60,8825683594 |||| Дефлятный (LVL = 9) || || 61.2258911133 ||
Как видно, большинство реализаций очень неэффективны: на процессорах Xeon E5-2650 высокого уровня дефтат составляет около 23 МБ/с, а даже GZIP составляет всего 33 МБ/с, что, вероятно, трудно удовлетворить. В то же время, самый быстрый алгоритм Defalte может достигать около 75 МБ/с, Snappy составляет 150 МБ/с, а LZ4 (Fast, реализация JNI) может достичь невероятного 320 МБ/с!
Из таблицы видно, что в настоящее время есть две реализации, которые находятся в недостатке: Snappy медленнее, чем LZ4 (быстрое сжатие), а сжатый файл больше. Напротив, LZ4 (высокий коэффициент сжатия) более медленнее, чем дефтат от 1 до 4, а размер выходного файла намного больше, чем у дефтата с уровня 1.
Поэтому, если вам нужно выполнить «сжатие в реальном времени», я обязательно выберу в реализацию JNI LZ4 (Fast) или Deflate Level 1. Конечно, если ваша компания не разрешает сторонние библиотеки, вы можете использовать только дефляцию. Вам также необходимо всесторонне рассмотреть, сколько существует бесплатных ресурсов ЦП и где хранятся сжатые данные. Например, если вы хотите сохранить сжатые данные для HDD, вышеупомянутая производительность 100 МБ/с вам не полезна (при условии, что ваш файл достаточно большой) - скорость жесткого диска станет узким местом. Если тот же файл выводит на жесткий диск SSD - даже LZ4 будет выглядеть слишком медленным перед ним. Если вы хотите сначала сжать данные, а затем отправить их в сеть, лучше всего выбрать LZ4, потому что производительность сжатия Deflate75MB/S действительно невелика по сравнению с пропускной способностью 125 МБ/с (конечно, я знаю, что в сетевом трафике также есть баоту, но даже если он включен, разрыв вполне значительно).
Суммировать
Если вы думаете, что сжатие данных очень медленное, вы можете рассмотреть реализацию LZ4 (Fast), которая может достичь скорости сжатия текста около 320 МБ/с - такая скорость сжатия не должна восприниматься для большинства приложений.
Если вы ограничены тем, что не можете использовать сторонние библиотеки или просто хотите иметь немного лучшее решение для сжатия, вы можете рассмотреть возможность использования JDK Defflate (lvl = 1) для кодирования и декодирования - тот же файл может сжать тот же файл до 75 МБ/с.
исходный код
Исходный код теста на сжатие Java