Представьте: у вас 100 файлов, 90% содержимого повторяется между ними. Вы пакуете их через tar | zstd. Получаете сжатие порядка 3.1x. А могли бы получить 10.0x.
Проблема в том, что обычная упаковка не знает, что файлы похожи. tar склеивает их в линейный поток и отдаёт компрессору. zstd работает в пределах окна и плохо сопоставляет одинаковые куски, если они разнесены по потоку на десятки мегабайт.
Идея maxpack в том, чтобы смотреть на набор файлов как на одно пространство данных и схлопывать повторы между файлами и версиями. Внутрь алгоритма сейчас не лезем, лучше посмотрим на цифры: на 100.1 МБ Node.js каждый новый мегабайт входа добавляет 26% нового содержимого, а на 10 ГБ — уже только 0.6%. Итоговая степень сжатия доходит до 50.1x. То есть на таких данных выигрыш растёт вместе с объёмом, а не упирается в потолок.
Где это полезно
Несколько версий одного проекта. Несколько git-тегов часто содержат много одинакового кода. Обычная упаковка проходит по каждой версии почти заново, а межфайловая дедупликация позволяет сохранить общие куски один раз.
Логи, дампы БД, CI-артефакты. Соседние выгрузки обычно отличаются на считанные проценты. Если архивировать их вместе, основная масса данных уже была увидена раньше.
Снапшоты блокчейн-нод. База состояния растёт, но соседние снапшоты часто сильно пересекаются по содержимому. В таком случае архив растёт в основном за счёт нового и изменившегося контента.
Версионируемые датасеты и производные артефакты. Наборы картинок, текстов, чекпойнты моделей и похожие артефакты часто живут версиями и содержат много повторов между ревизиями.
VM-снапшоты и контейнерные образы. Когда набор состоит из близких состояний одной системы, между ними обычно остаётся большой общий слой данных.
Фронтенд-сборки и статика. Хеши в именах файлов меняются, но большие фрагменты содержимого могут оставаться теми же. Если смотреть не на имена, а на байты, часть повторов можно вернуть.
Коротко: чем больше пересечение содержимого между файлами, тем интереснее становится такая схема.
Бенчмарки
Apple M4, один прогон, тёплый кэш. Это не single-core замеры: headline-прогоны CPython и Go для maxpack шли с MAXPACK_THREADS=4. Наборы Diverse, Similar и High-dedup здесь нужны затем, чтобы контролировать долю повторов. Это не случайно сгенерированные байты, а специально собранные тестовые наборы с понятной структурой.
Масштабирование: 100 МБ → 10 ГБ (Node.js v20)

Один проект (Node.js v20), постепенно добавляем теги. На 100.1 МБ все три метода дают ~3.9x. На 10 ГБ maxpack доходит до 50.1x, а tar+zstd и 7z остаются на ~4.8x и ~8.6x. С ростом данных доля уникального содержимого падает, и maxpack это использует.
Реальные проекты: CPython 3.12 и Go 1.23
А теперь уже не контролируемые наборы, а обычные исходники: 8 релизных тегов CPython 3.12 (v3.12.6 — v3.12.13), 817.3 МБ исходников. И 8 тегов Go 1.23, 974.8 МБ.

CPython 3.12
Метод | Архив | Сжатие | Pack | Unpack |
|---|---|---|---|---|
maxpack L3 | 31.0 МБ | 26.4x | 0.9 с | 3.1 с |
tar+zstd -3 | 215.3 МБ | 3.8x | 16.5 с | 11.9 с |
tar+zstd -19 | 170.3 МБ | 4.8x | 113.7 с | 17.0 с |
tar+gzip | 226.1 МБ | 3.6x | 32.6 с | 15.1 с |
tar+bzip2 | 191.0 МБ | 4.3x | 65.1 с | 23.9 с |
tar+xz | 168.3 МБ | 4.9x | 41.6 с | 13.7 с |
zip | 240.4 МБ | 3.4x | 26.4 с | 12.1 с |
tar+lz4 | 333.9 МБ | 2.5x | 21.9 с | 16.9 с |
На этом наборе maxpack даёт архив в 7 раз меньше, чем tar+zstd -3, а по времени упаковки и распаковки тоже оказывается впереди.
Go 1.23
Метод | Архив | Сжатие | Pack CPU | Unpack CPU |
|---|---|---|---|---|
maxpack L3 | 31.0 МБ | 31.4x | 14.4 с | 14.7 с |
tar+zstd -3 | 210.1 МБ | 4.6x | 40.5 с | 35.1 с |
tar+xz | 149.5 МБ | 6.5x | 294.0 с | 40.5 с |
7z -mx=9 | 70.7 МБ | 13.8x | 312.4 с | 11.4 с |
Здесь картина похожая: по размеру архива maxpack оказывается в 2.3 раза меньше 7z, а по CPU на упаковке заметно дешевле.
Несколько реальных versioned-проектов сразу

На fd, lsd и bat картина повторяется: чем больше версий и чем выше пересечение между ними, тем сильнее эффект. По размеру архива здесь выигрыш от 41.6% до 70.3% относительно обычной упаковки, а дедупликация доходит до 81.3%.
Степень сжатия на контролируемых наборах
Датасет | Метод | Размер архива | Сжатие | vs tar+zstd-3 |
|---|---|---|---|---|
Diverse (16 файлов, 1.8 МБ) | tar+zstd-3 | 327 775 | 5.6x | – |
maxpack L3 | 326 874 | 5.7x | -0.3% | |
Versions (30 файлов, 14 МБ) | tar+zstd-3 | 2 440 878 | 5.9x | – |
maxpack L3 | 2 471 756 | 5.8x | +1.3% | |
Similar (53 файла, 1.8 МБ) | tar+zstd-3 | 103 123 | 17.9x | – |
maxpack L3 | 102 774 | 17.9x | -0.3% | |
High-dedup (100 файлов, 90% дупликатов, 12 МБ) | tar+zstd-3 | 4 102 948 | 3.1x | – |
maxpack L3 | 1 290 154 | 10.0x | -68.6% | |
Big (60 файлов, 60 МБ) | tar+zstd-3 | 4 139 073 | 15.2x | – |
maxpack L3 | 4 017 578 | 15.7x | -2.9% |
Главная строка здесь — High-dedup. 100 файлов, из которых 90% содержимого совпадает: tar+zstd даёт 3.1x, maxpack — 10.0x. Архив получается в 3.2 раза меньше.
На наборах без выраженного межфайлового повтора виден паритет или небольшой overhead. Главный выигрыш начинается там, где есть повторяющееся содержимое между версиями и файлами.
Скорость
Датасет | Операция | maxpack | tar+zstd | Разница |
|---|---|---|---|---|
Diverse | Pack (L3) | 29 мс | 30 мс | 1.0x |
Diverse | Unpack | 21 мс | 32 мс | 1.5x быстрее |
Versions | Pack (L3) | 96 мс | 42 мс | 0.4x |
Versions | Unpack | 31 мс | 46 мс | 1.5x быстрее |
Similar | Pack (L3) | 35 мс | 36 мс | 1.0x |
Similar | Unpack | 24 мс | 42 мс | 1.8x быстрее |
High-dedup | Pack (L3) | 55 мс | 56 мс | 1.0x |
High-dedup | Unpack | 29 мс | 65 мс | 2.2x быстрее |
Big | Pack (L3) | 316 мс | 63 мс | 0.2x |
Big | Unpack | 71 мс | 120 мс | 1.7x быстрее |
Распаковка здесь стабильно быстрее: от 1.5x до 2.2x. По упаковке картина зависит от структуры данных. На Big (60 МБ, мало дупликатов) maxpack уже тратит время на анализ содержимого, который не успевает окупиться.
Итоговая оценка
Датасет | Дедупликация | Сжатие vs tar+zstd | Скорость pack | Совокупный |
|---|---|---|---|---|
Diverse | 6% | 1.00x | 1.0x | 1.0x |
Versions | 13% | 0.99x | 0.4x | 0.4x |
Similar | 6% | 1.00x | 1.0x | 1.0x |
High-dedup | 90% | 3.18x | 1.0x | 3.2x |
Big | 15% | 1.03x | 0.2x | 0.2x |
Если свести всё в одну фразу, она такая: чем больше повторов между файлами, тем больше выигрыш. Если повторов почти нет, чудес не происходит.
Где подход не окупается
Если данные в основном уникальны, межфайловая дедупликация почти ничего не находит.
Если набор маленький, служебные расходы и анализ содержимого могут съесть выигрыш.
Если нужен очень частый мелкий random access по байтам, такой формат удобен меньше, чем специализированное хранилище.
То есть это не «лучший упаковщик вообще для всего подряд», а инструмент под конкретный класс задач: версии, снапшоты и любые наборы с сильным пересечением содержимого.
Где это находится относительно других инструментов
По смыслу maxpack находится где-то между обычной упаковкой файлов и snapshot-системами вроде borg/restic:
Обычная упаковка (tar+zstd, 7z, zip) | Версионные (borg, restic) | maxpack | |
|---|---|---|---|
Дедупликация | Нет | Да, per-chunk | Да |
Solid compression | tar+zstd: да; 7z: да | Нет (per-chunk) | Да |
Результат | Один файл | Репозиторий | Один файл |
Инкрементальность | Нет | Да (снимки) | Да (append) |
Портативность | Высокая | Привязан к репо | Высокая |
Шифрование | Зависит от формата | Да | AES-256-GCM |
Обычные инструменты упаковки хорошо жмут поток, но не видят повторы между файлами. Snapshot-системы видят повторы, но обычно работают chunk-by-chunk и не ориентированы на один переносимый архив. maxpack — это попытка взять свойства обоих подходов в одном формате.
Как повторить у себя
Если хочется проверить идею на своих данных, достаточно сравнить две команды:
maxpack pack your_data/ -o test.maxpack tar cf - your_data/ | zstd -3 > test.tar.zst ls -lh test.maxpack test.tar.zst
Этого уже хватит, чтобы понять, есть ли в конкретном наборе межфайловый повтор, на котором такой подход окупается.
Публичный репозиторий с бинарными релизами и коротким smoke-скриптом (в процессе еще fuse, ffi уже есть): github.com/MaxPer2005/maxpack
