Perché gzip è lento nonostante le prestazioni della CPU e del disco rigido non vengano massimizzate?


14

Ho alcuni file JSON, 20 GB ciascuno, con cui voglio comprimere gzip:

gzip file1.json

Questo occupa un core CPU completo, tutto bene.

Elabora circa 25 MB / s (archiviato atop), il mio disco rigido è in grado di leggere 125 MB / se ho 3 core di processore gratuiti, quindi mi aspetto di ottenere una maggiore velocità durante la compressione di più file in parallelo. Quindi corro in altri terminali:

gzip file2.json
gzip file3.json
gzip file4.json

Sorprendentemente, il mio rendimento non aumenta; La CPU è circa il 25% su ogni core e il mio HD legge ancora solo 25 MB / s.

Perché e come affrontarlo?

Risposte:


17

L'ho scoperto:

Il motivo è che gzipopera (in termini di velocità della CPU rispetto alla velocità di ricerca HD in questi giorni) dimensioni del buffer estremamente basse .

Legge alcuni KB dal file di input, lo comprime e lo scarica nel file di output. Dato che ciò richiede una ricerca del disco rigido, è possibile eseguire solo poche operazioni al secondo.

Il motivo per cui la mia performance non è stata scalata è perché già si gzipcercava come un matto.


Ho risolto il problema utilizzando l' bufferutilità unix :

buffer -s 100000 -m 10000000 -p 100 < file1.json | gzip > file1.json.gz

Bufferando un sacco di input prima di inviarlo a gzip, il numero di piccole ricerche può essere drasticamente ridotto. Le opzioni:

  • -se -mdevo specificare la dimensione del buffer ( credo che sia in KB, ma non sono sicuro)
  • -p 100 si assicura che i dati vengano passati a gzip solo dopo aver riempito il buffer al 100%

Eseguendo quattro di questi in parallelo, ho potuto ottenere una velocità effettiva di 4 * 25 MB / s, come previsto.


Mi chiedo ancora perché gzip non permetta di aumentare le dimensioni del buffer - in questo modo, è piuttosto inutile se eseguito su un disco rotante.

EDIT : ho provato qualche altro comportamento nei programmi di compressione:

  • bzip2 elabora solo 2 MB / s a ​​causa della sua compressione più forte / più intensiva della CPU
  • lzop sembra consentire buffer più grandi: 70 MB / s per core e 2 core possono massimizzare il mio HD senza cercare eccessivamente

Puoi ddfare lo stesso?
Simon Kuang,

@SimonKuang Sospetto che ddpossa fare lo stesso con la sua bs=opzione, sì.
nh2,

Sembra una coincidenza interessante che per un singolo file la dimensione del blocco è riuscita a utilizzare completamente sia un singolo core della CPU che lo IOPS di un'unità.
Dave L.,

3

Dopo aver esaminato le prime cinque lezioni circa nel MIT OpenCourseware per 6.172: "Performance Engineering of Software Systems", ho eseguito l'analizzatore di prestazioni Linux 'perf' su un file di test moderatamente grande. Il risultato sembra mostrare le bancarelle della pipeline in cui un'istruzione deve attendere il risultato di una precedente.

       │         while (lookahead != 0) {                                                                
       │             /* Insert the string window[strstart .. strstart+2] in the                          
       │              * dictionary, and set hash_head to the head of the hash chain:                     
       │              */                                                                                 
       │             INSERT_STRING(strstart, hash_head);                                                 
  2.07 │       movzbl 0x8096d82(%edx),%eax                                                               
  3.99 │       mov    %edx,%ebp                                                                          
       │       shl    $0x5,%ecx                                                                          
  0.03 │       and    $0x7fff,%ebp                                                                       
  1.94 │       xor    %ecx,%eax                                                                          
  1.43 │       and    $0x7fff,%eax                                                                       
  2.01 │       mov    %eax,0x805e588                                                                     
  2.40 │       add    $0x8000,%eax                                                                      
  0.88 │       movzwl 0x8062140(%eax,%eax,1),%ecx                                                        
 23.79 │       movzwl %cx,%edi                                                                           
       │             /* Find the longest match, discarding those <= prev_length.  

La seconda ultima istruzione viene copiata %ecxe l'ultima deve attendere (arrestare la pipeline) fino a quando il %cxregistro non ha i dati pronti per l'uso. Questa stalla della pipeline sostiene il ciclo di contenimento.

Questo è il risultato di uno stile di programmazione C di vecchia scuola davvero oscuro.


1

Un consiglio che potrebbe portarlo a un altro livello di velocità su una CPU multi-core / hyperthreading:
(supponendo Ubuntu)

sudo apt-get install moreutils

moreutils contiene tra l'altro "gnu parallel" - che ha molte opzioni per aiutarti a utilizzare più della tua CPU.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.