Mi chiedevo se fosse possibile fare qualcosa di più efficiente della decompressione dall'inizio del file fino al punto. Sembra che la risposta sia no. Tuttavia, su alcune CPU (Skylake)zcat | tail
non aumenta la CPU alla massima velocità di clock. Vedi sotto. Un decodificatore personalizzato potrebbe evitare questo problema e salvare le chiamate di sistema di scrittura pipe e forse essere ~ 10% più veloce. (O ~ 60% più veloce su Skylake se non modifichi le impostazioni di risparmio energia).
Il meglio che potresti fare con uno zlib personalizzato con a skipbytes
funzione sarebbe quello di analizzare i simboli in un blocco di compressione per arrivare alla fine senza fare il lavoro di ricostruzione effettiva del blocco decompresso. Questo potrebbe essere significativamente più veloce (probabilmente almeno 2x) rispetto alla chiamata della normale funzione di decodifica di zlib per sovrascrivere lo stesso buffer e andare avanti nel file. Ma non so se qualcuno abbia scritto una simile funzione. (E penso che questo in realtà non funzioni a meno che il file non sia stato scritto appositamente per consentire al decoder di riavviarsi ad un certo blocco).
Speravo che ci fosse un modo per saltare i blocchi Deflate senza decodificarli, perché sarebbe molto più veloce. L'albero di Huffman viene inviato all'inizio di ogni blocco, quindi puoi decodificare dall'inizio di qualsiasi blocco (credo). Oh, penso che lo stato del decodificatore sia più che l'albero di Huffman, sono anche i precedenti 32 kB di dati decodificati, e questo non è ripristinato / dimenticato attraverso i limiti del blocco per impostazione predefinita. Gli stessi byte possono continuare a essere referenziati ripetutamente, quindi potrebbero apparire letteralmente solo una volta in un file compresso gigante. (ad es. in un file di registro, il nome host probabilmente rimane "attivo" nel dizionario di compressione per tutto il tempo e ogni sua istanza fa riferimento al precedente, non al primo).
Il zlib
manuale dice che devi usare Z_FULL_FLUSH
quando chiami deflate
se vuoi che lo stream compresso sia ricercabile a quel punto. "Reimposta lo stato di compressione", quindi penso che senza di ciò, i riferimenti all'indietro possono andare nei blocchi precedenti. Quindi, a meno che il tuo file zip non sia stato scritto con blocchi full-flush occasionali (come ogni 1G o qualcosa avrebbe un impatto trascurabile sulla compressione), penso che dovresti fare più del lavoro di decodifica fino al punto desiderato di quanto non fossi inizialmente pensiero. Immagino che probabilmente non puoi iniziare all'inizio di nessun blocco.
Il resto è stato scritto mentre pensavo che sarebbe stato possibile trovare solo l'inizio del blocco contenente il primo byte desiderato e decodificare da lì.
Sfortunatamente, l'inizio di un blocco Deflate non indica quanto è lungo , per i blocchi compressi. I dati incomprimibili possono essere codificati con un tipo di blocco non compresso che ha una dimensione di 16 bit in byte nella parte anteriore, ma i blocchi compressi no: RFC 1951 descrive il formato in modo abbastanza leggibile . I blocchi con codifica Huffman dinamica hanno l'albero nella parte anteriore del blocco (quindi il decompressore non deve cercare nel flusso), quindi il compressore deve aver mantenuto l'intero blocco (compresso) in memoria prima di scriverlo.
La massima distanza di riferimento all'indietro è di soli 32 kB, quindi il compressore non deve conservare molti dati non compressi in memoria, ma ciò non limita la dimensione del blocco. I blocchi possono essere lunghi più megabyte. (Questo è abbastanza grande perché il disco cerchi di valerne la pena anche su un'unità magnetica, rispetto alla lettura sequenziale in memoria e semplicemente saltando i dati nella RAM, se fosse possibile trovare la fine del blocco corrente senza analizzarlo).
zlib crea blocchi il più a lungo possibile:
secondo Marc Adler , zlib avvia un nuovo blocco solo quando il buffer dei simboli si riempie, che con l'impostazione predefinita è 16.383 simboli (letterali o corrispondenze)
Ho decompresso l'output di seq
(che è estremamente ridondante e quindi probabilmente non è un ottimo test), ma pv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c
su quello funziona solo a ~ 62 MiB / s di dati compressi su uno Skylake i7-6700k a 3,9 GHz, con RAM DDR4-2666. Sono 246 MiB / s di dati decompressi, che è una variazione di chump rispetto alla memcpy
velocità di ~ 12 GiB / s per blocchi di dimensioni troppo grandi per adattarsi alla cache.
(Con energy_performance_preference
l'impostazione predefinita balance_power
invece di balance_performance
, il regolatore della CPU interno di Skylake decide di funzionare solo a 2,7 GHz, ~ 43 MiB / s di dati compressi. Uso sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
per ottimizzarli. Probabilmente tali chiamate di sistema così frequenti non sembrano legate alla CPU reale lavorare all'unità di gestione dell'alimentazione.)
TL: DR: zcat | tail -c
è associato alla CPU anche su una CPU veloce, a meno che tu non abbia dischi molto lenti. gzip ha usato il 100% della CPU su cui ha funzionato (e ha eseguito 1,81 istruzioni per clock, secondo perf
), e ha tail
usato 0,162 della CPU su cui ha funzionato (0,58 IPC). In caso contrario, il sistema era principalmente inattivo.
Sto usando Linux 4.14.11-1-ARCH, che ha KPTI abilitato di default a aggirare Meltdown, quindi tutte quelle write
chiamate di sistema gzip
sono più costose di quanto non fossero: /
Avere la ricerca integrata unzip
o zcat
(ma ancora usando la normale zlib
funzione di decodifica) salverebbe tutte quelle scritture di pipe e farebbe funzionare le CPU Skylake alla massima velocità di clock. (Questo downclocking per alcuni tipi di carico è univoco per Intel Skylake e versioni successive, che hanno scaricato il sistema decisionale sulla frequenza della CPU dal sistema operativo, poiché dispongono di più dati su ciò che la CPU sta facendo e possono aumentare / diminuire più rapidamente. normalmente buono, ma qui porta Skylake a non accelerare a tutta velocità con una regolazione più conservatrice del governatore).
Nessuna chiamata di sistema, solo riscrivere un buffer che si adatta alla cache L2 fino a raggiungere la posizione di byte iniziale desiderata, probabilmente farebbe almeno una differenza del qualche%. Forse anche il 10%, ma sto solo inventando numeri qui. Non ho profilato zlib
in alcun dettaglio per vedere quanto è grande il footprint della cache e quanto il flush TLB (e quindi il flush della cache uop) su ogni chiamata di sistema fa male con KPTI abilitato.
Esistono alcuni progetti software che aggiungono un indice di ricerca al formato di file gzip . Questo non ti aiuta se non riesci a convincere nessuno a generare file compressi ricercabili per te, ma altri futuri lettori potrebbero trarne vantaggio.
Presumibilmente nessuno di questi progetti ha una funzione di decodifica che sa saltare un flusso Deflate senza un indice, perché sono progettati per funzionare solo quando un indice è disponibile.