Я задавался вопросом, можно ли сделать что-нибудь более эффективное, чем распаковка от начала файла до момента. Похоже, что ответ - нет. Однако на некоторых процессорах (Skylake) процессор zcat | tail
не разгоняется до полной тактовой частоты. См. ниже. Пользовательский декодер может избежать этой проблемы и сохранить системные вызовы записи канала, и может быть на ~ 10% быстрее. (Или ~ 60% быстрее на Skylake, если вы не настраиваете параметры управления питанием).
Лучшее, что вы могли бы сделать с настраиваемым zlib с skipbytes
функцией - это проанализировать символы в блоке сжатия, чтобы добраться до конца, не выполняя работу по фактической реконструкции распакованного блока. Это может быть значительно быстрее (возможно, по крайней мере, в 2 раза), чем вызов обычной функции декодирования zlib для перезаписи того же буфера и перемещения вперед в файле. Но я не знаю, написал ли кто-нибудь такую функцию. (И я думаю, что это на самом деле не работает, если файл не был написан специально, чтобы позволить декодеру перезапуститься с определенного блока)
Я надеялся, что есть способ пропустить блоки Deflate без их декодирования, потому что это будет намного быстрее. Дерево Хаффмана отправляется в начале каждого блока, так что вы можете декодировать с начала любого блока (я думаю). О, я думаю, что состояние декодера больше, чем дерево Хаффмана, это также предыдущие 32 КБ декодированных данных, и это не сбрасывается / не забывается через границы блоков по умолчанию. На одни и те же байты можно постоянно ссылаться, поэтому они могут появляться буквально один раз в гигантском сжатом файле. (например, в файле журнала имя хоста, вероятно, остается «горячим» в словаре сжатия все время, и каждый его экземпляр ссылается на предыдущий, а не на первый).
В zlib
руководстве говорится, что вы должны использовать Z_FULL_FLUSH
при вызове, deflate
если вы хотите, чтобы сжатый поток был доступен для поиска до этого момента. Он «сбрасывает состояние сжатия», поэтому я думаю, что без этого обратные ссылки могут перейти в предыдущий блок (ы). Таким образом, если ваш zip-файл не был написан со случайными блоками полного сброса (например, каждый 1G или что-то еще не оказало бы незначительного влияния на сжатие), я думаю, вам придется выполнять большую часть работы по декодированию до того уровня, который вы хотите, чем я был изначально мышление. Я думаю, что вы, вероятно, не можете начать в начале любого блока.
Остальное было написано, пока я думал, что можно будет просто найти начало блока, содержащего первый нужный байт, и декодировать оттуда.
Но, к сожалению, начало блока Deflate не указывает, как долго это будет , для сжатых блоков. Несжимаемые данные могут быть закодированы с несжатым типом блока, который имеет 16-битный размер в байтах на передней панели, но сжатые блоки этого не делают: RFC 1951 описывает формат довольно читабельно . Блоки с динамическим кодированием Хаффмана имеют дерево в передней части блока (поэтому декомпрессор не должен искать в потоке), поэтому перед записью компрессор должен сохранить весь (сжатый) блок в памяти.
Максимальное расстояние обратного отсчета составляет всего 32 кБ, поэтому компрессору не нужно хранить много несжатых данных в памяти, но это не ограничивает размер блока. Блоки могут быть длиной в несколько мегабайт. (Это достаточно для того, чтобы поиск диска стоил того даже на магнитном диске, по сравнению с последовательным считыванием в память и просто пропуском данных в ОЗУ, если можно было найти конец текущего блока, не анализируя его).
zlib создает блоки как можно дольше:
согласно Марку Адлеру , zlib начинает новый блок только тогда, когда заполнен буфер символов, который по умолчанию равен 16 383 символам (литералам или совпадениям)
Я распаковал вывод seq
(который чрезвычайно избыточен и, следовательно, вероятно, не очень хороший тест), но pv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c
на этом он работает со скоростью ~ 62 МБ / с сжатых данных на Skylake i7-6700k с частотой 3,9 ГГц и оперативной памятью DDR4-2666. Это 246 МБ / с декомпрессированных данных, что является частичным изменением по сравнению со memcpy
скоростью ~ 12 ГБ / с для блоков слишком большого размера, чтобы поместиться в кэш.
(С energy_performance_preference
набором по умолчанию balance_power
вместо balance_performance
губернатор внутреннего процессора Skylake решает только работать на 2.7GHz, ~ 43 MiB / с сжатыми данными. Я использую , sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
чтобы настроить его. Возможно , такие частые системные вызовы не выглядят как настоящие CPU переплет работать с блоком управления питанием.)
TL: DR: zcat | tail -c
привязан к процессору даже на быстром процессоре, если только у вас не очень медленные диски. gzip использовал 100% процессора, на котором он работал (и выполнял 1,81 инструкции в такт, согласно perf
), и tail
использовал 0,162 процессора, на котором он работал (0,58 IPC). В остальном система в основном простаивала.
Я использую Linux 4.14.11-1-ARCH, в которой по умолчанию включен KPTI для обхода Meltdown, поэтому все эти write
системные вызовы gzip
обходятся дороже, чем раньше: /
Наличие встроенного поиска для unzip
или zcat
(но все еще использующего обычную zlib
функцию декодирования) сохранит все эти записи канала и заставит процессоры Skylake работать на полной тактовой частоте. (Этот разгон для некоторых видов нагрузки является уникальным для Intel Skylake и более поздних версий, которые переносят процесс принятия решений о частоте ЦП из ОС, потому что у них больше данных о том, что делает ЦП, и они могут увеличивать / уменьшать скорость быстрее. как правило, хорошо, но здесь приводит к тому, что Skylake не разгоняется до полной скорости при более консервативной настройке регулятора).
Никакие системные вызовы, просто переписывание буфера, который помещается в кэш L2 до тех пор, пока вы не достигнете желаемой позиции начального байта, вероятно, будет иметь разницу как минимум на несколько%. Может быть, даже 10%, но я просто придумываю цифры здесь. Я не стал zlib
подробно рассказывать, насколько велика его площадь кэш-памяти и сколько сбрасывает TLB (и, следовательно, очищает uop-cache) при каждом системном вызове при включенном KPTI.
Есть несколько программных проектов, которые добавляют индекс поиска в формат файла gzip . Это не поможет вам, если вы не сможете заставить кого-либо создавать для вас сжатые файлы, которые могут быть найдены для поиска, но другие будущие читатели могут выиграть.
Предположительно ни один из этих проектов не имеет функции декодирования , которая знает , как пропустить через поток Deflate без индекса, потому что они предназначены только для работы , когда индекс является доступен.