Иван предложил интересную идею, как ускорить процесс арифметического сжатия. Долго я ломал голову, как бы его оптимальней реализовать. В конце концов получилось, сейчас поделюсь результатами, а вот детали реализации чуть позже, скорее всего когда вернусь из поездки. Там интересный алгоритм получается.
Итак, сжимал я большой текстовый файл, самый большой, что смог найти в своей свалке на компьютере.
Почему текстовый файл? Потому что я хочу сравнить, какой вариант сжатия эффективнее для текстовых данных в различных представлениях: на 8 или 16-битном алфавите?
Мои грубые прикидки на основе кода Хаффмана вроде показывают, что сжатие с 16-битным алфавитом всегда как минимум не хуже, а на 16-битных текстовых данных (2-байтный Unicode) всегда лучше. Но и медленнее. Мне любопытно, как будет себя в этом смысле вести арифметический кодек.
Но всё это будет как-нибудь потом.
Пока что результаты. Коэффициент сжатия русскоязычного текста оказался примерно 1.6, что очень неплохо, я считаю, для чисто энтропийного сжатия. Если перед этим пройтись по тексту словарным сжатием, то получится очень может получится очень достойный вариант.
В первую очередь я оттестировал свой вариант, смесь двух таблиц: с чистыми частотами встреч символов и с накоплением.
Скорость сжатия оказалась 5.4 МиБ/c, скорость распаковки 1.3 МиБ/с. Столь большая разница связана с тем, что при распаковке нам нужно иметь готовую всю таблицу частот с накоплением, поэтому оптимизация с их частичным расчетом тут не прокатывает.
Зато данные в таблице с накоплением находятся в отсортированном порядке, поэтому использование двоичного поиска вместо линейного сразу улучшило результат распаковки: 2 МиБ/с.
После чего я протестировал вариант без отдельной таблицы с частотами с накоплениями. Расчет был на то, что таблица обычных частот (без накопления) в два раза меньше по размеру, чем с накоплением, поэтому за счет уменьшения количества записей в память возможно производительность улучшиться.
Но это предположение не оправдалось. Сжатие без частот с накоплением дало всего лишь 3.9 МиБ/с, поэтому делать вариант распаковки даже не стал, как бесперспективный.
Ну а теперь с результаты работы программы с использованием пирамидальной структуры, предложенной Иваном: сжатие – 7.5, распаковка – 4.6 МиБ/с. Соответственно, прирост в 1.4 раза для распаковки и в 2.2 раза для сжатия! Что ж, очень неплохо вышло.
Интересно, оптимизировав на ассемблере, смогу ли выйти за 20 МиБ/с? Ах да, все замеры выполнял на своем стандартном PileDriver FX-4350.
Еще надо попробовать потестить на бинарных данных, вдруг какой глюк вылезет с символами #0 или #255.
Из любопытного: более медленный метод сжимает чуть лучше быстрого. Дело в том, что точность у него чуть меньше, поэтому приходится на больших данных частоты делить на два во избежания переполнения. В силу этого модель более адаптивна, и если в разных частях файла частоты распределения символов отличаются, то она даст лучшей результат, что и случилось.
Впрочем, для того, что бы получить сравнимый эффект более быстрым методом, можно сжимать данные не целиком, а сравнительно небольшими блоками, частично огрубляя модель данных (распределение частоты символов) при переходе от блока к блоку.