Гонка кодеков почти закончена: очередной процент у LZMA, BWT и контекстных моделей стоит кратного времени. Запас остался в другом — понимать, что за данные лежат внутри. Nova Prism разбирает JPEG, PDF, zip, MP3 и WAV на части, сжимает их заново и собирает обратно бит в бит.
На уже сжатых данных мы впереди всех, включая CM, и быстрее их. На обычных — впереди LZ и на несколько процентов позади CM.
Сканер deflate смотрел только на начало файла, поэтому контейнер, который не объявляет себя с первого байта — git packfile, полезная нагрузка установщика, закрытый формат с потоками zlib внутри, — считался несжимаемым. Теперь кандидаты ищутся по всему файлу: три packfile этого репозитория −13,7%, на 11,8% меньше 7-Zip, а все прочие корпуса побайтово те же.
Появились пакеты .deb, .rpm, AppImage и статический
np-cli (glibc 2.34+), а окно само сообщает о новой версии — ссылкой на файл
именно для вашей системы.
Перед 1.0 код прошёл три раунда аудита, где каждую находку пытался опровергнуть
отдельный проверяющий. Закрыты: испорченный блок, который 0.9.6 в редком случае записывал
под верной контрольной суммой (архивы 0.9.6 стоит проверить
np-cli test), запись за пределами буфера в декодере bsc на подделанном
архиве, обрезка последнего поколения при гниении футера и манифест, способный запросить
8 ГБ памяти. Третий раунд искал не поломки, а МОЛЧАНИЕ, и нашёл его
четыре раза: окно называло успехом упаковку, из которой часть файлов выпала, открывало
повреждённый архив без единого слова, считало удачной распаковку, не записавшую ни одного
файла, и теряло пути с именем не в UTF-8.
Последним закрылся не изъян кодека, а единственный ответ на вопрос, которого окно не
задавало: распаковка всегда шла в режиме «пропустить существующие», поэтому восстановление
поверх живого дерева не записывало ничего. Теперь окно спрашивает — и
только когда есть о чём: если ни одна цель не занята, диалога нет и путь побайтово
прежний. «Заменить» — первый случай, когда окно просит перезапись, и он вскрыл дыру:
сторонние читатели zip и 7z не проверяли, не является ли цель самим открытым архивом.
report.zip с записью report.zip внутри, распакованный на месте,
стирал сам себя и отчитывался об успехе. Проверка добавлена в оба читателя.
Оглавление zip хранит счётчик файлов 16 битами, а смещение оглавления — 32. Когда их не хватает, туда пишут заглушку, а правду кладут в отдельную запись ZIP64. Заглушка принималась за настоящее смещение, сканер находил ноль потоков, и файл ложился в архив как есть — без единого сообщения.
Дело оказалось не в архивах больше 4 ГБ: пакеты приложений Windows
(.msix, .appx) пишут ZIP64 всегда, независимо
от размера, и ни один такой пакет на этой машине не пережимался ни разу — самый мелкий
найден на 344 КБ. Теперь оба сканера читают запись по её собственному признаку:
app.appx −38,3%,
Microsoft.SecHealthUI.appx −30,3%. Кодек и турнир этот снимок не тронул.
Все преобразователи машинного кода, какие есть, трогают ровно три команды:
E8, E9 и 0F 8x, то есть относительный адрес
перехода. А на 64-битном x86 есть второе такое же повсеместное 32-битное смещение —
адресация относительно счётчика команд, через которую идёт каждое обращение к глобальной
переменной и каждый вызов через таблицу импорта. Перепись 10,4 ГБ установленных программ
объясняет, почему это стало важно только сейчас: 96,9% байт исполняемых файлов —
64-битные. Фильтр 42 забирает и эти адреса: .NET
−9,8%, Firefox −6,2%, Wireshark −3,3%, Go −2,3%.
Итог двух версий: девять настоящих установок (Firefox, .NET,
Wireshark, Go, Python, две на Electron, 2,74 ГБ) шли на 5,91% хуже 7z -mx9,
потом на 1,77%, а теперь на 0,23% лучше.
Одним архивом на всё дерево — на 4,5% меньше и на 13% быстрее, чем в 0.9.3.
Цена — совместимость в одну сторону: архив 0.9.5 с исполняемыми файлами старая версия не откроет и скажет об этом прямо, а 0.9.5 читает всё, что было написано раньше. Поднять предел 64 МиБ значит менять формат, и цена измерена: на обычных данных бо́льшая единица уже вредит — Silesia +1,10% при 48 МиБ.
Ответ оказался неожиданно узким. У .exe отставание было 15,1% — и
по одному файлу мы на 0,10% МЕНЬШЕ 7-Zip: весь проигрыш давали девять
программ инструментария Go, в каждую из которых влинкована одна и та же среда исполнения.
Один и тот же код лежал в наборе девять раз, а увидеть это можно, только если он попадает
в одно окно. У .pak (интерфейсные строки Chromium) отставание было 29,6%, и
там весь проигрыш — одна пара почти одинаковых наборов языковых файлов, лежавших в 56 МБ
друг от друга. Ни то, ни другое не про кодек.
Отсюда план: блок с машинным кодом вырос до 64 МиБ, пары одноимённых файлов ищутся от
96 КБ вместо мегабайта. После этого на переписи по типам проигрывали только
.exe и .dll — их забрала 0.9.5.
По каждому корпусу — насколько наш архив меньше, чем у лучшего из всех остальных, а не просто чем у 7-Zip. Вправо от середины — мы впереди.
Верхние четыре строки — данные, которые принято считать законченными: остальным применить к ним нечего, а нам есть что. Пятая показывает границу: FLAC не берёт никто, включая нас. Нижние четыре — прямая гонка кодеков; отставание на дереве исходников и установленной программе — плата за возможность править архив по одному файлу, которой у сплошного потока 7-Zip нет.
Это не «лучше настроенный LZMA». Это данные, которые остальные считают законченными и складывают как есть: Nova Prism разворачивает сжатие и делает его заново — и обходит здесь даже CM-класс, будучи при этом быстрее.
.msix и .appx — тоже zip, но настоящие числа оглавления лежат
у них в записи ZIP64. Сканер её не читал и складывал пакет как есть. Теперь читает, и на
настоящих пакетах: 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% — внутри почти нечего пережимать. Каждый файл распакован и сверен с
исходником побайтово.
Счётчик записей и смещение оглавления в классическом хвосте zip — 16 и 32 бита; когда числа туда не влезают, стандарт требует писать заглушку, а правду класть в ZIP64. Считалось, что это касается только больших архивов. Оказалось неверно вдвое: упаковщик Microsoft пишет ZIP64 всегда, и все пять системных пакетов, найденных на этой машине, устроены именно так — от 344 КБ.
Множество форматов хранит внутри потоки zlib без всякой сигнатуры: git packfile, полезная
нагрузка установщиков, ресурсы программ. Раньше сканер отправлял файл по первым байтам,
и такой контейнер писался как есть — у 7-Zip и xz там тоже всего −2,2%. Теперь
кандидаты ищутся по всему файлу: три packfile этого репозитория (4 355 190 Б) —
7z -mx9 4 260 799, nova было 4 355 856, стало
3 760 190 Б, −13,7%. Silesia, enwik8,
дерево исходников, корпус deflate, установленные программы и наборы .pak —
побайтово без изменений.
Случайный заголовок zlib встречается в двоичных данных примерно раз на 4 КиБ, поэтому голый поток распознаётся по заголовку и проверяется распаковкой, а в контейнерный фильтр файл попадает, только если потоки покрывают не меньше 95% просмотренного. Счёт вместо покрытия стоил дереву исходников +6,4%.
Под словом «звук» скрываются три разные задачи, и ответы у них разные: несжатый 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. На дереве исходников решает не кодек, а устройство архива. Там, где формат уже энтропийно закодирован, турнир не проводится: моделировать нечего — библиотека FLAC собирается байт в байт та же за 43 секунды вместо 306.
Проза и исходный код для программы одинаковы — и то и другое «текст», — а кодеки им нужны противоположные. Поэтому выбирает не правило, а отбор на пробе самой единицы: на 83 реальных единицах LZMA2 съедал 61% всего времени турнира, выигрывая 46% единиц; проба убирает большую часть этой работы, не меняя результат ни на байт.
По таблице LTCB решать было нельзя: там bsc считает весь гигабайт одним блоком на 5 ГБ памяти, а у нас единица сжатия 32 МиБ, и её размер — не настройка, а цена правки одного файла. Замер на нашей геометрии показал, что BWT выигрывает на тексте и проигрывает на дереве файлов — ровно тот случай, который решает турнир. libbsc внедрён кодеком 4 на максимальном и среднем уровне; почему не на быстром — ниже.
Важнее не «во сколько раз быстрее», а меняется ли размер архива от числа потоков. Большинство архиваторов распараллеливаются тем, что режут данные на более мелкие независимые блоки: каждый добавленный поток там оплачен степенью сжатия. У nova размер единицы задан геометрией архива, поэтому вывод обязан быть байт в байт одинаковым на одном потоке и на восьми.
18 отчётов в 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.7 — версия выпущена 20 сентября 2026 года девятью файлами: установщик и
np-cli.exe для Windows, .deb, .rpm, AppImage и
статический np-cli для Linux, install.sh, инструкция и
SHA256SUMS. Это последняя бета перед 1.0 и первая во всей линейке 0.9,
выложенная обычным релизом, а не пред-релизом. Изменились разделы 1, 3,
10 и 11; цифры турнира кодеков и эталонных корпусов те же, что в прошлом снимке. Архивы,
созданные 0.9.6, стоит проверить командой np-cli test — почему, сказано в
карточке 0.9.7 выше.