The Daily Newsstand · Free, Always
Tuesday, September 22, 2026

УЛЬТИМАТИВНОЕ сжатие кириллицы (и не только)

Translate

Каждое русское слово в интернете платит налог на кириллицу. Ваш телеграм-чат, логи сервера (если в них правда попала кириллица), кириллический код в, прости господи, 1С — всё это занимает в два раза больше места, чем могло бы. И вам об этом никто не сказал.

Быстрый ликбез для тех, кто не в курсе: большая часть текста в современном интернете существует в кодировке UTF-8. Она основана на Unicode.

Когда мы пишем на английском, всё отлично — первый байт Юникода как раз отведен под английский, цифры и спецсимволы. Таким образом, их размер при передаче по сети — 1 байт на символ.

Но когда дело касается кириллицы, она в однобайтовый диапазон Юникода не поместилась — для кодирования каждой кириллической буквы используется два байта.

Банально: «Привет» весит 12 байт, а «Privet» — всего 6 байт.

Старожилы могут вспомнить, как при наборе SMS лимит символов съедался русским языком в ×2, и простая замена русской “о” на английскую “o” позволяла отправить больше текста в одном SMS. Но это лирика для стариков.

Когда-то раньше люди уже искали решение этой проблемы — создавали такие кодировки, как KOI-8 и Win-1251. Они умели кодировать кириллицу в 1 байт, но имели большие проблемы с универсальностью.

Пластмассовый мир победил, Unicode оказался сильней. По сути, специфичная русская кодировка не поддерживала «общепринятый» Unicode, и требовалось постоянно указывать, в какой, собственно, кодировке читать тот или иной документ.

И вот недавно я нашел решение: CL-8.

Это кодировка второго уровня поверх UTF-8 с прозрачным фоллбеком. То есть те символы, что поддерживаются кодировкой, кодируются компактно, а те, что не поддерживаются (например, фрагмент на китайском), передаются как чистый Unicode — с одним управляющим байтом на всю последовательность символов. Эта передача и зовется заморским словом фоллбек.

Звучит непонятно. Я раз 15 переписал это описание, чтобы было понятно — всё равно понятнее не стало.

Покажу на конкретных примерах.

А чтобы вам не было скучно, знайте — в конце вас ждёт издевательство над «Войной и миром» великого Льва Николаевича Толстого. Она уже издевалась над нами — сегодня наш черед.

Здесь и далее приводятся выдержки из примеров, доступных в examples в репозитории кодировки.

Мы уже говорили про «Привет». Разберем «Привет мир».

--- Russian (basic Cyrillic) ---
Original        : "Привет мир"
UTF-8 length    : 19 bytes (10 chars)
CL-8 length     : 10 bytes
Savings vs UTF-8: 47.4%

UTF-8 длина для «Привет мир» составляет 19 байт — 9 кириллических букв по 2 байта и один байт на пробел.

Собранный в CL-8 вариант весит 10 байт — почти в два раза меньше.

При этом важно уточнить: это работает не только для русского. Кодировка без перехода в фоллбек поддерживает 38 языков — практически все кириллические и латинские.

О том и название — CL-8: кириллица, латиница, 8 бит. Кириллица на первом месте.

Посмотрим на другие интересные примеры с русским:

--- Russian (longer prose) ---
Original        : "Это тестовый текст для проверки эффективности сжатия."
UTF-8 length    : 99 bytes (53 chars)
CL-8 length     : 53 bytes
Savings vs UTF-8: 46.5%
--- Russian (pangram fragment, ё) ---
Original        : "Съешь ещё этих мягких булок"
UTF-8 length    : 50 bytes (27 chars)
CL-8 length     : 27 bytes
Savings vs UTF-8: 46.0%
--- Russian (Ё/ё — capitalization exception) ---
Original        : "Ёлка ёлка"
UTF-8 length    : 17 bytes (9 chars)
CL-8 length     : 9 bytes
Savings vs UTF-8: 47.1%
--- Russian (alternating case) ---
Original        : "ЙоЖмАк"
UTF-8 length    : 12 bytes (6 chars)
CL-8 length     : 6 bytes
Savings vs UTF-8: 50.0%
--- Russian (all caps, direct 1-byte) ---
Original        : "ПРИВЕТ МОСКВА"
UTF-8 length    : 25 bytes (13 chars)
CL-8 length     : 13 bytes
Savings vs UTF-8: 48.0%

Эффективность сжатия, как видно из приведенного лога, находится в пределах 46–50 процентов.

И это уже можно применять не только для красивых бенчмарков. Например, для хранения русскоязычных сообщений, логов, JSON-данных или передачи текста между сервисами. CL-8 позволяет уменьшить объем данных еще до того, как в дело вступят обычные алгоритмы сжатия.

Что до других языков? Их есть у меня.

Взглянем на белорусский, украинский и болгарский:

--- Ukrainian (ґ, є, і) ---
Original        : "ґанок, єдність, місто"
UTF-8 length    : 38 bytes (21 chars)
CL-8 length     : 21 bytes
Savings vs UTF-8: 44.7%
--- Belarusian (ў via breve, і) ---
Original        : "Беларусь: ў, і"
UTF-8 length    : 24 bytes (14 chars)
CL-8 length     : 15 bytes
Savings vs UTF-8: 37.5%
--- Bulgarian (Cyrillic) ---
Original        : "Болгария столица"
UTF-8 length    : 31 bytes (16 chars)
CL-8 length     : 16 bytes
Savings vs UTF-8: 48.4%

С применением специализированных букв эффективность снижается — некоторые из них даже в CL-8 требуют по два байта. Но всё еще остается высокой.

Еще примеры — языки Центральной Азии:

--- Kazakh (8 extra letters) ---
Original        : "Қазақ әліпбиі: ғ, қ, ң, ө, ү, ұ, һ"
UTF-8 length    : 53 bytes (34 chars)
CL-8 length     : 35 bytes
Savings vs UTF-8: 34.0%
--- Kyrgyz (ң, ө, ү) ---
Original        : "Кыргыз тили: ң, ө, ү"
UTF-8 length    : 33 bytes (20 chars)
CL-8 length     : 20 bytes
Savings vs UTF-8: 39.4%
--- Uzbek Cyrillic (ў, қ, ғ, ҳ) ---
Original        : "Ўзбекистон: ў, қ, ғ, ҳ"
UTF-8 length    : 36 bytes (22 chars)
CL-8 length     : 25 bytes
Savings vs UTF-8: 30.6%
--- Tajik (ҷ, ҳ, ӣ, ӯ) ---
Original        : "Тоҷикистон: ҷ, ҳ, қ, ғ, ӣ, ӯ"
UTF-8 length    : 44 bytes (28 chars)
CL-8 length     : 28 bytes
Savings vs UTF-8: 36.4%

И естественно, не забудем о братьях наших сербах:

--- Serbian Cyrillic (6 extra letters) ---
Original        : "Србија: ј, љ, њ, ћ, ђ, џ"
UTF-8 length    : 36 bytes (24 chars)
CL-8 length     : 24 bytes
Savings vs UTF-8: 33.3%

В общем и целом, даже в случаях, когда эффективность падает из-за расширения алфавита кодирования, она всё еще остается весьма высокой.

Теперь про латиницу.

На базовом английском экономии нет — полный паритет с Unicode:

Original        : "API JSON HTTP"
UTF-8 length    : 13 bytes (13 chars)
CL-8 length     : 13 bytes
Savings vs UTF-8: 0.0%

Но только вот латинские языки — это не только чистая латиница, как в английском. И тут у нас есть чего предложить, с нашего славянского стола — к их немецкому:

--- German (ß, ä) ---
Original        : "Straße Käse"
UTF-8 length    : 13 bytes (11 chars)
CL-8 length     : 12 bytes
Savings vs UTF-8: 7.7%
--- German (ü) ---
Original        : "Grüße aus München"
UTF-8 length    : 20 bytes (17 chars)
CL-8 length     : 19 bytes
Savings vs UTF-8: 5.0%

Некоторые буквы немецкого алфавита тоже не попали в блатную однобайтовую часть Unicode.

Да, экономия в 5% — это не так круто, как в 50, но тоже не лишнее. Особенно когда речь идет о больших объемах текстовых данных, которые постоянно гоняются между сервисами или хранятся в базе.

Короткие примеры — это весело, но пора переходить к главному блюду. Расчехляем волыну и пристраиваемся поплотнее к более серьезному тексту.

Нашим подопытным будет «Война и мир». Почти 3 миллиона знаков смеси французского с нижегородским.

normalized       5161180 bytes
encoded          2918791 bytes in 21.155625ms
savings:         43.4%
decoded          5161180 bytes in 19.129875ms

Сжатие такого большого текста на моем стареньком M2 Mac заняло 21 миллисекунду

Дало 43.4 процента экономии.

Обратное раскодирование заняло 19 миллисекунд.

5-мегабайтный текст стал 3-мегабайтным.

И вот здесь уже появляются вполне прикладные сценарии. CL-8 можно использовать как промежуточный слой перед обычным gzip или Brotli, хранить так текстовые поля в системах с большим объемом данных или уменьшать сетевой трафик между сервисами. А лучше всё и сразу.

Особенно интересен вариант с небольшими сообщениями: чатами, событиями, логами, payload’ами API. Там классический компрессор может оказаться избыточным из-за собственного служебного оверхеда, а CL-8 просто переводит поддерживаемые символы в однобайтовое представление без добавления служебных данных.

Единственный нюанс — подобные тексты пока требуют предварительной Unicode-нормализации, так как могут содержать некорректные символы, типа русской «о» со знаком ударения.

Теперь самое вкусное — что происходит, если после CL-8 еще сверху применить обычное сжатие.

Итог сравнения:

Компрессор   Raw → размер   CL-8 → размер   CL-8 меньше на   Raw время   CL-8 время
gzip -6      1 414 445      1 175 007       −16.9%            0.292s      0.110s
gzip -9      1 380 689      1 173 866       −15.0%            0.585s      0.115s
brotli -q6   1 314 799      1 052 196       −20.0%            0.131s      0.079s
brotli -q11  980 741        914 257         −6.8%             6.418s      3.253s

bzip2 -9     900 820        868 450         −3.6%             0.285s      0.158s

Примечание — bzip2 в вебе не используется, добавлен как самый эффективный алгоритм из популярных. Остальные используются в вебе.

Сравнивали сжатие классическими алгоритмами оригинального (нормализованного) текста и его же после CL-8 энкодинга. Работали на той же «Войне и мире».

Ключевой момент — размер после сжатия для закодированного текста получается меньше. В зависимости от алгоритма выигрыш составил от 6 до 20 процентов.

Но что важнее — ускорение самого сжатия за счет сокращения исходной базы. Для рантайм-сжатия это может быть критично: иногда скорость важнее нескольких лишних килобайт.

И еще один момент. Классические алгоритмы сжатия на совсем маленьких объемах могут увеличивать размер за счет собственного оверхеда. CL-8 с вами так не поступает — он работает непосредственно с представлением текста и не требует отдельного компрессора.

Поэтому для сообщений в мессенджере, небольших JSON, логов и других коротких текстовых payload’ов это особенно интересный вариант.

Ну и вишенка на торте — крейт на Rust для этой кодировки весит всего 21 килобайт.

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.