Автор: Алексей Федорчук
Подавляющее большинство пользователей Unix-подобных систем хранят свои архивы в форматах tar.gz или tar.bz. В том же виде распространяется и большинство ориентированных на них материалов — от исходных текстов программ до подборок документации. Есть ли резон изменить эту традицию?
Вступление
Не так давно в новостях наткнулся на пропущенное мной ранее
Собственно, всё вышесказанное послужило для меня поводом проверить, а столь ли эффективен LZMA для компрессии данных? Ведь за
Надо заметить, сборки этой программы существуют и для основных дистрибутивов Linux, а также для FreeBSD, универсальная POSIX-версия p7zip для всех UNIX/Linux (правда, только с командным интерфейсом), не говоря уже о возможности собрать её из исходников в любой системе. Но, как и большинство относительно «старых» POSIX’ивистов, я привык к комбинации tar с gzip или bzip2 (по ситуации и под настроение), и использовать нечто новое мне не хотелось. А тут предлагается задействовать механизм LZMA традиционным способом — указанием соответствующей опции команде tar.
Однако непосредственно измерениям компрессии предшествовали некоторые подготовительные действия. Для начала потребовалось поставить сам пакет lzma — как известно, tar, будучи чистым архиватором, своего механизма компрессии не имеет, а использует таковые соответствующих подключаемых утилит. Так вот, в штатном комплекте Zenwalk утилиты lzma не обнаруживается — её надо вытащить из репозитория (через netpkg или его графический фронт-энд).
Далее… Не знаю, где как, а в Zenwalk, в котором я в тот момент находился, утилита tar представлена ныне версией 1.16, возможностью подключения lzma ещё не обладавшей. Так что потребовалось скачать последние её
Теперь для применения LZMA-компрессии при создании tar-архива достаточно указать соответствующую опцию:
$ tar --create --lzma --file filename.tar.lzma path2/arch_dir
Или, в более употребимой простыми людьми краткой форме:
$ tar cJf filename.tar.lzma path2/arch_dir
где опция J и представляет собой алиас для полной формы —lzma. Если присваивать архивному файлу суффикс по правилам утилиты tar, опцию J можно заменить на a (что эквивалентно полной форме —auto-compress), обеспечивающей определение типа компрессии по «расширению» *.lzma.
Распаковка lzma-компрессированного архива выполняется в обратном порядке:
$ tar xJf filename.tar.lzma
или
$ tar xaf filename.tar.lzma
Более того, скажу по секрету: если архив именован по правилам, то можно опустить даже опцию —auto-compress — она и так будет задействована по умолчанию.
Приключения на btrfs
Тем, кто приключений не любит и при этом не интересуется еще и btrfs, рекомендуется сразу перейти к следующему разделу.
Вот теперь можно и оценить несравненные достоинства LZMA-компрессии — разумеется, в сравнении с традиционными механизмами gzip и bzip2. В качестве объекта сравнения выступали:
- единичный html-файл размером 1,6 Мбайт;
- дерево портежей Gentoo — множество мелких файлов суммарным объемом 154 Мбайт;
- iso-образ установочного диска Bluewhite64 версии 12.2, файл объемом 658 Мбайт;
- развернутое с предыдущего образа дерево, суммарным объемом 656 Мбайт.
Сначала я хотел ограничиться только сравнением первой пары (html и portage), но, как будет видно чуть ниже, результаты оказались довольно странными, и я решил дополнить их данными другого типа. Волей случая это оказался Bluewhite64 — я давно собирался познакомиться с этим 64-битным портом классической Slackware, и потому образ его установочного диска просто лежал у меня скачанным под рукой.
Всё это хозяйство располагалось на тестовом разделе с файловой системой btrfs. Казалось бы, причём тут файловая система, если оценивается эффективность компрессии. Косвенно, но оказалось, что причём, хотя это и другая история.
Единичные файлы (html и iso) просто подвергались компрессии различных типов, посредством утилит gzip, bzip2 и lzma, на всякий случай — с явным указанием максимальной степени сжатия, то есть с ключём -9. Оба файловых набора (портежи и дерево файловой иерархии) собирались в компрессированные tar-архивы tar.gz, tar.bz2 и tar.lzma со сжатием посредством ключей z, j и J, соответственно.
Результаты приведены в таблице 1, и честно говоря, части, касающейся lzma, не впечатляют.
Таблица 1. Сравнение размеров архивов, btrfs
Файл, каталог | Исходный | gzip | bzip2 | lzma |
html, Кбайт | 1611 | 568 | 408 | 436 |
iso, Мбайт | 658 | — | 529 | 608 |
portage, Мбайт | 154 | 46 | 33 | 30 |
File system, Мбайт | 656 | 629 | 635 | 621 |
Можно видеть, что при компрессии единичных файлов, вне зависимости от объема и наполнения, lzma даже проигрывает bzip2, а на наборах файлов показывает лишь незначительное превосходство. Незначительное настолько, что и говорить о нём смешно. При этом на единичных файлах и bzip2, и lzma в одном случае обе-две проигрывают gzip, а в другом на месте последней красуется многозначительный прочерк — к нему нам ещё предстоит вернуться.
Ещё веселее станет, если посмотреть на временные затраты компрессии и декомпрессии по различным методикам. Компрессия и декомпрессия html-файла проходила так быстро, что измерять её просто не имело смысла. Однако первый звоночек — если при использовании gzip и bzip2 она действительно была мгновенной, но lzma-сжатие всё-таки занимало некоторый чувственноуловимый интервал времени.
При архивации и компрессии дерева портежей затраты времени были таковы:
$ time tar czf portage.tar.gz portage/
real 0m8.755s
user 0m7.984s
sys 0m1.417s
$ time tar cjf portage.tar.bz2 portage/
real 0m37.761s
user 0m35.598s
sys 0m1.594s
$ time /usr/local/bin/tar cJf portage.tar.lzma portage
real 4m23.040s
user 4m18.897s
sys 0m1.630s
Можно видеть, что если bzip-компрессия в несколько раз медленней компрессии gzip (что, собственно, общеизвестно), то при использовании lzma затраты времени увеличиваются уже на порядок по отношению к bzip2, и на полтора порядка — сравнительно с gzip.
Разархивация и декомпрессия дерева портежей протекает так:
$ time tar xzf portage.tar.gz
real 1m11.199s
user 0m2.228s
sys 0m13.397s
$ time tar xjf portage.tar.bz2
real 0m30.244s
user 0m8.887s
sys 0m13.877s
$ time /usr/local/bin/tar xJf portage.tar.lzma
real 0m22.728s
user 0m3.912s
sys 0m13.413s
Здесь картина оказывается прямо противоположной: компрессированный посредством lzma архив разворачиваетсячуть быстрее, чем архив bzip2, а архив gzip отстаёт от обоих в два-три раза. Первый результат был ожидаем — высокая скорость декомпрессии всегда отмечалась как особенность lzma. А вот провал gzip был неожиданным.
Объяснение его было получено в следующем туре — при компрессии дерева Bluewhite64/ и файла его iso-образа. В первом случае я получил такое:
$ time tar czf Bluewhite64.tar.gz Bluewhite64/
real 0m39.872s
user 0m33.315s
sys 0m2.104s
$ time tar cjf Bluewhite64.tar.bz2 Bluewhite64/
real 2m48.955s
user 2m42.479s
sys 0m3.399s
$ time tar cJf Bluewhite64.tar.lzma Bluewhite64/
real 9m37.376s
user 9m27.458s
sys 0m2.160s
Что в принципе совпадало с картиной, полученной ранее. С той только разницей, что здесь нарастание затрат времени от gzip через bzip2 к lzma проявилось ещё рельефнее.
А вот на компрессии iso-файла произошёл первый сбой. Началось всё хорошо и с предсказуемым результатом:
$ time bzip2 -k -9 Bluewhite64-12.2-install-d1.iso
real 2m50.444s
user 2m43.551s
sys 0m3.062s
$ time lzma -k -9 Bluewhite64-12.2-install-d1.iso
real 14m26.181s
user 14m7.017s
sys 0m2.217s
А вот при gzip-компрессии система замерла на долгое время. Такое, что я успел не только покурить, но и основательно заскучать. Настолько, что перешёл в другое терминальное окно и дал команду
$ ps aux
из вывода которой всё стало мучительно ясно:
USER PID ... TTY STAT START TIME COMMAND
...
alv 5176 ... pts/1 D+ 18:14 0:13 gzip ...
То есть процесс gzip превратился в зомби, не убиваемого никаким kill -9. Более того, машина отказывалась корректно перезагружаться или выключаться — ни через используемый мной по умолчанию gdm, ни из консоли прямой командой halt. Так что в итоге пришлось прибегнуть к Reset’у.
Несколько опечалившись, я, тем не менее, решил вместо этого выполнить декомпрессию ранее упакованного bzip2 образа. С тем же самым, плачевным, результатом — зомбирование соответствующего процесса. Более того, с того момента мне в этом каталоге не удалось выполнить ни одной операции компрессии или декомпрессии никакой из трёх утилит. И даже просмотреть содержимое каталога с тестовыми файлами при наличии этих зомбированных процессов (до их истребления путём холодной перезагрузки) командой ls тоже не удавалось — последняя сама превращалась в зомби, в полном соответствие с учением вудуистских колдунов. Правда, процесс ls при этом убить с помощью kill -9 всё-таки получалось.
Рассмотрение вывода команды ps показало, что кроме строки вроде
alv 5206 ... pts/1 D+ 18:14 0:13 bunzip2 ...
в нём наличествовали ещё и такие:
root 5210 ... ? S< 18:14 0:00 [btrfs-worker-1]
root 5211 ... ? S< 18:14 0:00 [btrfs-worker-2]
Не привязанные ни к какому терминалу, они также оказались неубиваемыми. При этом проверка файловой системы btrfs проходила успешно:
# btrfsck /dev/sda7
found 6560182282 bytes used err is 0
total csum bytes: 5736840
total tree bytes: 686186496
btree space waste bytes: 180050442
file data blocks allocated: 5874524160
referenced 5873704960
Btrfs Btrfs v0.18
В скобках замечу, что в версии btrfs 0.18, включённой в ядро 2.6.29-rc2 (и, предполагаю, также и в появившееся позднее ядро rc-3), резулярные ошибки сегментации при запуске btrfsck наконец исчезли — если сначала собрать ядро, а потом уже btrfs-progs соответствующей версии, также 0.18.
Тем не менее, аномалии в поведении btrfs появились — впервые с того момента, как я начал с ней экспериментировать. И это поставило под сомнение данные, касающиеся сравнения времени компрессии по различным алгоритмам. В связи с чем все измерения пришлось повторить на разделе с традиционной файловой системой.
В качестве традиционно-ориентированной файловой системы в данном случае выступила etx3fs с параметрами монтирования по умолчанию (плюс noatime). Объекты издевательств остались прежними — только дерево Bluewhite64 я заменил на развернутые исходники только скачанного ядра linux-2.6.29-rc3 (как более соответствующие реальным условиям). Результаты по размеру компрессированных файлов приведены в таблице 2.
Таблица 2. Размеры архивов, ext2fs
Обьект | Исходный | gzip | bzip2 | lzma |
iso | 658 | 629 | 635 | 608 |
portage | 154 | 47 | 34 | 30 |
linux | 371 | 71 | 55 | 46 |
kernel.org | — | 68 | 54 | — |
Интересно, что размеры компрессированных архивов в файловых системах ext3fs и btrfs не совпали (за исключением единичного html-файла) — для сжатого iso’шника весьма ощутимо. Не совпали и полученные размеры архивов ядра Linux tar.gz и tar.bz2 с оригинальными с
В целом для наборов файлов картина вполне закономерная и ожидаемая: lzma-архивы компактней bz2-аналогов на 10-20%, и примерно в полтора раза меньше gz-архивов. С единичными файлами среднего и большого объема картина менее определённая, и существенно зависит от их внутреннего устройства. Впрочем, кто нынче сжимает единичные большие файлы? Ведь, как правило, это всякая мультимедиа — а она и так уже сжата до упора.
Теперь посмотрим, чего нам это стоит с точки зрения затрат времени. Упаковка дерева портежей в этом отношении выглядит так:
$ time tar czf portage.tar.gz portage/
real 0m9.153s
user 0m8.016s
sys 0m1.011s
$ time tar cjf portage.tar.bz2 portage/
real 0m36.777s
user 0m35.656s
sys 0m0.988s
$ time tar cJf portage.tar.lzma portage/
real 4m24.884s
user 4m22.519s
sys 0m1.028s
То есть рост затрат времени — в разы, при увеличении степени сжатия в лучшем случае на десятки процентов.
С декомпрессией архива портежей картина такая:
$ time tar xzf portage.tar.gz
real 0m21.563s
user 0m2.242s
sys 0m2.960s
$ time tar xjf portage.tar.bz2
real 0m22.900s
user 0m10.853s
sys 0m3.831s
$ time tar xJf portage.tar.lzma
real 0m19.482s
user 0m3.939s
sys 0m3.145s
То есть механизм gzip всё равно оказывается самым быстрым, lzma — на втором месте, bzip2 идёт затыкающим. Хотя разрыв между вошедшими в тройку сильнейших (при трёх участниках) составляет не более пары секунд.
На упаковке единичного iso’шника — картина аналогичная, близкая к геометрической прогрессии в направлении от gzip через bzip2 к lzma. С той только разницей, что здесь выигрыш в степени компресии lzma супротив остальных не превышает пяти процентов.
$ time gzip -9 Bluewhite64-12.2-install-d1.iso
real 0m35.456s
user 0m33.414s
sys 0m1.243s
$ time bzip2 -9 -k Bluewhite64-12.2-install-d1.iso
real 2m46.061s
user 2m43.602s
sys 0m1.272s
$ time lzma -9 -k Bluewhite64-12.2-install-d1.iso
real 14m15.390s
user 14m10.060s
sys 0m2.010s
Кстати, и степень сжатия по сравнению с исходным файлом — не больше. Посему время декомпресии я мерять поленился — зачем декомпрессировать то, что не имеет ни малейшего смысла компрессировать?
Наконец, последний штрих — манипуляции с деревом исходников ядра Linux. Время компрессии — без неожиданностей:
$ time tar czf linux.tar.gz linux-2.6.29-rc3/
real 0m15.227s
user 0m14.347s
sys 0m0.513s
$ time tar cjf linux.tar.bz2 linux-2.6.29-rc3/
real 0m57.778s
user 0m56.349s
sys 0m0.691s
$ time tar cJf linux.tar.lzma linux-2.6.29-rc3/
real 5m27.196s
user 5m19.434s
sys 0m1.271s
То есть опять за вполне ощутимый выигрыш в степени компрессии мы платим более чем ощутимым увеличением времени, на неё затрачиваемым.
$ time tar xzf linux.tar.gz
real 0m6.259s
user 0m2.576s
sys 0m1.171s
$ time tar xjf linux.tar.bz2
real 0m13.699s
user 0m12.360s
sys 0m1.225s
$ time tar xJf linux.tar.lzma
real 0m6.551s
user 0m5.082s
sys 0m1.071s
При декомпрессии — повторение картины, наблюдавшейся с деревом портежей.
Интересен вопрос о ресурсах машины, затрачиваемых при операциях компрессии по различным алгоритмам. Точнее, как мы сейчас увидим, ничего интересного в нём нет. Однако читатели, желающие прослыть дюже умными, редко когда упустят случая задать его при обсуждении подобных материалов — как будто ресурсы их машины не превышают их потребности на порядок, и им, сиротам, приходится бороться за каждый квант процессорного времени.
Так вот, предвосхищая подобный вопрос, привожу слепки типичной загруженности процессора в ходе выполнения архивации и компрессии ядра Linux по механизмам:
1) gzip
2) bzip2
3) lzma
Ни малейшей разницы, не так ли? Единственный вывод, который можно сделать из этих картинок — ни один из механизмов компрессии не обеспечивает равномерного распределения нагрузки на два наличествующих процессора.
Правда, существует реализация механизма bzip2, оптимизированная для SMP-систем —
Так что от Глюкавого всё это…
Выводы
… предоставляю делать читателю. Моё скромное мнение таково: при создании личных архивов, которые будут использоваться локально, применяемый механизм декомпресии никакого рояля не играет. Разве что некий материал надо впихивать, всовывать и плотно утрамбовывать на носитель ограниченной ёмкости — тут уж действительно придётся от натуги сплёвывать и lzma использовать. Но такие ситуации нынче достаточно редки — чай, не в век флопиков живём.
А вот изготовлять lzma-архивы для общественного применения — пожалуй, что смысл и есть. Таким образом можно сэкономить ближним толику трафика или времени на скачивание. Ну а самому организовать упаковку в фоновом режиме.
Аналогично, и самому при выборе доступных для скачивания вариантов архивов нужно обращать внимание на те, что имеют суффикс *.tar.lzma. Только вот что-то не видел я пока в сети таких архивов…