Гонка кодеков почти закончена: LZMA, BWT и контекстные модели вылизывают четвёртое десятилетие, и очередной процент там стоит кратного времени. Запас остался в другом месте — в том, чтобы понимать, что за данные лежат внутри. Nova Prism разбирает JPEG, PDF, zip, MP3 и WAV на части, сжимает их заново и собирает обратно бит в бит. Ниже — что из перспективных технологий это подтвердило замерами, а что отвергло.
Коротко: на уже сжатых данных мы впереди всех, включая CM, и быстрее их. На обычных данных — впереди LZ, отстаём от CM на несколько процентов.
На данных, которые можно разобрать по формату, мы впереди всех, включая CM, и быстрее. На обычных данных — впереди семейства LZ (7-Zip, xz, brotli), но пока позади CM (kanzi, zpaqfranz).
Что изменилось с прошлого снимка: разрыв на установленных программах
не просто закрыт — он перевернулся. Девять настоящих установок (Firefox, .NET,
Wireshark, Go, Python, две на Electron, 2,74 ГБ) шли на 5,91% хуже
7z -mx9, в прошлом снимке на 1,77%, а теперь на
0,23% лучше. Одним архивом на всё дерево —
на 4,5% меньше и на 13% быстрее, чем в 0.9.3.
Дал это разбор, который начался с вопроса «какой ТИП файлов проигрывает», а не «какая
установка». Ответ оказался неожиданно узким. У .exe отставание было 15,1%
— и по одному файлу мы на 0,10% МЕНЬШЕ 7-Zip, а весь проигрыш давали
девять программ инструментария Go, в каждую из которых влинкована одна и та же среда
исполнения: один и тот же код лежал в наборе девять раз, а увидеть это можно, только
если он попадает в одно окно. У .pak (интерфейсные строки Chromium)
отставание было 29,6%, и там весь проигрыш — одна пара почти одинаковых наборов
языковых файлов, лежавших в 56 МБ друг от друга. Ни то, ни другое не про кодек.
Отсюда план 0.9.4: блок с машинным кодом вырос до 64 МиБ, пары одноимённых файлов
ищутся от 96 КБ вместо мегабайта. После этого на переписи по типам проигрывали только
.exe и .dll — и вот их 0.9.5 и забрал.
Все преобразователи машинного кода, какие есть, трогают ровно три команды:
E8, E9 и 0F 8x, то есть относительный адрес
перехода. А на 64-битном x86 второе такое же повсеместное 32-битное смещение — это
адресация относительно счётчика команд, через которую идёт каждое обращение к
глобальной переменной и каждый вызов через таблицу импорта; её не выносит в отдельный
поток никто. Перепись 10,4 ГБ установленных программ на этой машине
объясняет, почему это стало важно только сейчас: 96,9% байт исполняемых файлов —
64-битные. Новый фильтр (id 42) забирает и эти адреса: .NET −9,8%,
Firefox −6,2%, Wireshark −3,3%, Go −2,3%. Цена — совместимость в одну сторону:
архив 0.9.5 с исполняемыми файлами старая версия не откроет и скажет об этом прямо,
а 0.9.5 читает всё, что было написано раньше. Поднять предел выше 64 МиБ значит менять формат — это решение
владельца, и оно измерено: на обычных данных бо́льшая единица уже вредит
(Silesia +1,10% при 48 МиБ), так что цена такого шага выросла, а не упала.
0.9.6 нашёл слепой участок не в кодеке, а в том, что сканер вообще ВИДИТ. Классическое
оглавление zip хранит счётчик файлов 16 битами и смещение оглавления — 32; когда их не
хватает, туда пишут заглушку, а правду кладут в отдельную запись ZIP64, которую мы не
читали. Заглушка принималась за настоящее смещение, сканер находил ноль потоков, и файл
ложился в архив как есть — без единого сообщения. Дело оказалось не в архивах больше
4 ГБ: пакеты приложений Windows (.msix, .appx) пишут ZIP64
всегда, независимо от размера, и ни один такой пакет на этой машине не
пережимался ни разу — самый мелкий найден на 344 КБ. Теперь оба сканера, распаковки
контейнера и разбора на члены, читают запись по её собственному признаку, а не по
заглушке: app.appx −38,3%, Microsoft.SecHealthUI.appx −30,3%. Обычные
архивы также стали разбираться на уже сжатые данные на уровне по умолчанию, а не только
на максимуме — тот же потолок, что читатель уже допускал, просто раньше не применялся к
решению «пережимать ли». Кодек и турнир этот снимок не тронул — ни одна цифра в разделах
ниже не изменилась.
По каждому корпусу — насколько наш архив меньше, чем у лучшего из всех остальных, а не просто чем у 7-Zip. Вправо от середины — мы впереди.
Верхние четыре строки — данные, которые принято считать законченными: остальным применить к ним нечего, а нам есть что. Пятая показывает границу: FLAC не берёт никто, включая нас, и это измерено до самого потолка. Нижние четыре — обычные данные, где идёт прямая гонка кодеков. Дерево исходников и установленная программа — плата за возможность править архив по одному файлу: 7-Zip сжимает всё одним сплошным потоком, поэтому заменить внутри него отдельный файл нельзя.
Это не «лучше настроенный LZMA». Это данные, которые остальные считают законченными и складывают как есть. Nova Prism разворачивает сжатие и делает его заново — и обходит на них даже CM-класс, будучи при этом быстрее.
.msix и .appx — тоже zip, но особого вида: счётчик записей и
смещение оглавления в классическом хвосте формата 16- и 32-битные, а когда файлов или
байт больше, чем туда влезает, стандарт требует писать заглушку и класть настоящие числа
в отдельную ZIP64-запись. Наш сканер эту запись не читал и принимал заглушку за
настоящее смещение — находил ноль потоков и складывал файл как есть, без единого
сообщения. Считалось, что это касается только архивов больше 4 ГБ; оказалось неверно
вдвое — упаковщик Microsoft пишет ZIP64 всегда, вне зависимости от
размера, и все пять системных пакетов, найденных на этой машине, устроены именно так,
от 344 КБ.
Измерено на настоящих пакетах, максимальный уровень: app.appx
(1 199 354 Б) — было 953 429, стало 588 435 Б,
−38,3%; Microsoft.SecHealthUI.appx (10 262 607 Б) — было 7 816 188,
стало 5 447 431 Б, −30,3%. Выигрыш есть не
везде: маленький Microsoft.Copilot.msix дал только −1,0%, потому что внутри
почти нечего пережимать. Каждый файл распакован и сверен с исходником побайтово.
Под словом «звук» скрываются три разные задачи, и ответы у них разные: несжатый WAV, уже сжатый FLAC и MP3 ведут себя совершенно по-разному.
По дороге нашлось отставание от 7-Zip на несжатом звуке — 28%. Кодек был ни при чём: разницу делал фильтр разности, который у нас был, но не запускался — проверка «и так сжимается» пропускала его мимо, а WAV сжимается до 82%. Без проверки: −22%, FLAC сверху — ещё −19%.
Сам FLAC пережимать смысла нет: лучший энкодер даёт −0,94%, а потолок для контекстной модели (paq8px) — 4,9% ценой в 700 раз медленнее допустимого. Не стоит того.
Правило простое: не «не медленнее 7-Zip на максимуме», а быстрее него и меньше него одновременно. Это сразу отсекает верх таблицы рекордов — не по качеству, а по скорости: cmix распаковывает enwik9 около 75 часов, nncp сжимает его 2,8 суток на RTX 4090. Такие программы — потолок избыточности, а не цель. Одно исключение оставлено намеренно: на уже сжатых файлах мы тратим в 11 раз больше 7-Zip и отдаём взамен на 30% меньший архив — там разменивать время на байты и есть смысл всей работы.
enwik9, 1 ГБ. По вертикали — размер архива, чем выше точка, тем он меньше; по горизонтали — скорость сжатия, шкала логарифмическая. Точки в кольце — nova, 7-Zip, xz, kanzi и zpaqfranz, снятые для этого снимка в одном прогоне на восьми потоках: они сравнимы друг с другом напрямую. Именно здесь и видно ускорение — наша точка ушла вправо и вверх сразу: 61,5 секунды вместо 238,9 и 189,4 МБ вместо 195,0, а распаковка гигабайта заняла 12,6 секунды против 143,3 у kanzi -l9 и 565,4 у zpaqfranz -m5. Остальные точки — из Large Text Compression Benchmark: один поток, другое железо, поэтому по горизонтали к ним стоит относиться как к порядку величины, а не к проценту. Наши точки по той же причине стоят правее, чем стояли бы в один поток.
enwik8, enwik9 и Silesia — потому что по ним публикуют результаты все, и наши размеры можно класть рядом с чужими без оговорок.
На максимальном уровне за каждую единицу сжатия соревнуются четыре кодека, и остаётся меньший результат. Универсального победителя нет — вот кто сколько единиц забрал. Гонять всех четверых целиком, впрочем, незачем: сначала проходит отбор на пробе самой этой единицы, и полный проход делают только те, у кого на пробе есть шанс.
Отсюда видно, почему вопрос «какой кодек лучше» не имеет ответа. На тексте всё забирает BWT. На исполняемых файлах и документах выигрывает LZMA2, но заметную долю берёт PPMd7 порядка 16. На дереве исходников решает не кодек, а устройство архива. Там, где формат уже энтропийно закодирован, турнир не проводится: PPMd7 моделирует контексты символов, а их там не осталось — библиотека FLAC собирается байт в байт та же за 43 секунды вместо 306.
И по той же причине класс данных не годится в качестве правила: проза и исходный код для программы одинаковы — и то и другое «текст», — а кодеки им нужны противоположные. На 83 реальных единицах LZMA2 съедал 61% всего времени турнира, выигрывая 46% единиц; отбор на пробе убирает большую часть этой работы, не меняя результат ни на байт.
По таблице LTCB решать было нельзя: там bsc считает весь гигабайт одним блоком на 5 ГБ памяти, а у нас единица сжатия 32 МиБ, и её размер это не настройка, а цена правки одного файла. Замер на нашей геометрии показал, что BWT выигрывает на тексте и проигрывает на дереве файлов — ровно тот случай, который решает турнир. libbsc внедрён кодеком 4 на максимальном и среднем уровне; быстрый его не получил, и ниже видно почему.
Проверяются две вещи, и вторая важнее. Первая — во сколько раз быстрее. Вторая — меняется ли размер архива от числа потоков. Большинство архиваторов распараллеливаются тем, что режут данные на более мелкие независимые блоки: каждый добавленный поток там оплачен степенью сжатия. У nova размер единицы задан геометрией архива, поэтому вывод обязан быть байт в байт одинаковым на одном потоке и на восьми.
16 отчётов в docs/research/ плюс собственные замеры. Строки
отвергнуто ценны не меньше остальных: каждая — это неделя,
которую не потратят снова.
Порядок — по измеренной ценности, а не по интересности.
Все замеры сняты на одной машине — 8 логических ядер, Windows 11 — одной командой на корпус: внутри таблицы инструменты сравнимы напрямую. Между таблицами сравнимы размеры, но не время: корпуса снимались в разные прогоны. Версии эталонов: 7-Zip 26.02, xz 5.6, brotli 1.1, kanzi 2.5.1 (сборка на C++), zpaqfranz 64.8, paq8px v216.
Эталонные корпуса — enwik8, enwik9 и Silesia — взяты по ссылкам выше в исходном виде. Корпус «уже сжатого» собирается скриптом из файлов с постоянными ссылками, и рядом записываются их SHA-256, так что он воспроизводится побайтово. Остальные наборы — документы, фотографии и музыка — пока собраны локально; они помечены отдельно, и на публичные источники их ещё предстоит перевести.
Чужие цифры на диаграмме размера против скорости — из Large Text Compression Benchmark (Matt Mahoney) и Hutter Prize; они сняты на другом железе и в один поток, поэтому по горизонтали к ним стоит относиться как к порядку величины.
Снимок 0.9.6. Ни одна цифра сжатия или скорости на этой странице не изменилась с
прошлого снимка — 0.9.6 не трогал турнир кодеков. Что изменилось — раздел 1 и раздел 3:
ZIP64 теперь читают оба сканера, разбора уже сжатого и разбора контейнера на члены, и
пакеты приложений Windows на этой машине наконец пережимаются
(app.appx −38,3%, SecHealthUI
−30,3%) — раньше не пережимался ни один, молча. Обычные архивы
разбираются на уже сжатое на уровне по умолчанию, а не только на максимуме: тот же
.tar.gz, который раньше выходил БОЛЬШЕ исходника (96,37%), теперь
−26,66%. Появился профиль -p core|media|max — не «насколько
сильно жать», а «чем разрешено пользоваться», важно там, где архив открывает не сама
программа, а маленький встроенный код. Закрыта уязвимость: специально собранный вложенный
zip мог попросить у сканера около 6 ГБ памяти и уронить процесс — она была уже в 0.9.5;
теперь разбор ограничен по построению, а не подобранным числом, и ни один настоящий архив
не изменился ни на байт. Программа впервые собрана и проверена на Linux — 199 тестов,
дерево после упаковки и распаковки побайтово то же самое. И оба файла программы
переименованы: командная строка nova.exe стала np-cli.exe, окно
nova-prism.exe стало novaprism.exe.