Мені було цікаво, чи можна зробити щось більш ефективне, ніж розпаковувати від початку файлу до моменту. Здається, що відповідь - ні. Однак на деяких процесорах (Skylake) zcat | tail
не розширює процесор до повної тактової частоти. Дивіться нижче. Спеціальний декодер може уникнути цієї проблеми і зберегти виклики системи запису труб та, можливо, бути на 10% швидшим. (Або на 60% швидше в Skylake, якщо ви не налаштовуєте налаштування управління енергією).
Найкраще, що ви могли зробити з налаштованим zlib з skipbytes
функцією, було б проаналізувати символи в блоці стиснення, щоб дійти до кінця, не роблячи роботи над реконструкцією декомпресованого блоку. Це може бути значно швидше (можливо, принаймні 2 рази), ніж викликати звичайну функцію декодування zlib, щоб перезаписати той самий буфер і рухатися вперед у файл. Але я не знаю, чи хтось написав таку функцію. (Я думаю, що це насправді не працює, якщо файл був написаний спеціально, щоб дозволити перезапуск декодера в певному блоці).
Я сподівався, що існує спосіб пропустити через блокування Deflate, не розшифровуючи їх, тому що це буде набагато швидше. Дерево Хаффмана надсилається на початку кожного блоку, тому ви можете розшифрувати з початку будь-якого блоку (я думаю). О, я думаю, що стан декодера більше, ніж дерево Хаффмана, це також попередні 32 Кб декодованих даних, і це не за замовчуванням скидається / забуто через межі блоків. На ті самі байти можна постійно посилатися, тому вони можуть відображатися буквально один раз у гігантському стисненому файлі. (наприклад, у файлі журналу ім'я хоста, ймовірно, залишається "гарячим" у словнику стиснення, і кожен його примірник посилається на попередній, а не на перший).
У zlib
посібнику сказано, що вам потрібно користуватися Z_FULL_FLUSH
під час дзвінка, deflate
якщо ви хочете, щоб стислий потік знаходився до цього моменту. Він "скидає стан стиснення", тому я думаю, що без цього зворотні посилання можуть переходити до попереднього блоку. Отже, якщо ваш zip-файл не був написаний з випадковими повнорозмірними блоками (як кожен 1G або щось щось мало б незначний вплив на стиснення), я думаю, вам доведеться виконати більше роботи над розшифровкою до потрібної точки, ніж я був спочатку мислення. Я думаю, ви, мабуть, не можете почати будь-який блок.
Решта цього написана, поки я думав, що можна буде просто знайти початок блоку, що містить перший байт, який ви хочете, і розшифрувати звідти.
Але, на жаль, запуск блоку відхилення не вказує на тривалість стиснених блоків. Нестислимі дані можуть бути кодовані типом блоку без стиснення, який має 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,7 ГГц, ~ 43 Мбіт / с стислих даних. Я використовую його sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
для налаштування. Можливо, такі часті системні дзвінки не схожі на реальні пов'язані з процесором робота з енергоуправлінням.)
TL: DR: zcat | tail -c
CPU пов'язаний навіть у швидкому процесорі, якщо у вас дуже повільні диски. 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 (і, таким чином, загально-кеш-флеш) при кожному системному виклику болить при включеному KPTI.
Існує декілька програм, які додають індекс пошуку у формат файлу gzip . Це не допоможе вам, якщо ви не можете змусити когось генерувати для вас стиснуті файли, але інші майбутні читачі можуть отримати користь.
Імовірно ні один з цих проектів не має функції декодування , яка знає , як пропустити через потік Deflate без індексу, тому що вони призначені тільки для роботи , коли індекс є доступний.