Por que o gzip é lento apesar de o desempenho da CPU e do disco rígido não estar no máximo?

10

Eu tenho alguns arquivos JSON, cada um com 20 GB, que quero compactar com gzip :

gzip file1.json

Isso ocupa um núcleo completo da CPU, tudo bem.

Ele processa cerca de 25 MB / s (verificado em atop ), meu disco rígido pode ler 125 MB / se eu tenho 3 núcleos de processadores livres, então espero obter uma aceleração ao compactar vários arquivos em paralelo . Então eu corro em outros terminais:

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

Surpreendentemente, minha taxa de transferência não aumenta; A CPU está em torno de 25% em cada núcleo, e meu HD ainda lê apenas 25 MB / s.

Por que e como lidar com isso?

    
por nh2 24.05.2013 / 06:37

3 respostas

14

Eu descobri:

A razão é que gzip opera (em termos de velocidade de CPU versus velocidade de busca HD atualmente) tamanhos de buffer extremamente baixos .

Ele lê alguns KB a partir do arquivo de entrada, comprime-o e libera-o para o arquivo de saída. Dado o fato de que isso requer uma busca por disco rígido, apenas algumas operações podem ser feitas por segundos.

O motivo pelo qual o meu desempenho não foi dimensionado é porque já um gzip estava procurando como um louco.

Eu trabalhei em torno disso usando o utilitário unix buffer :

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

Por armazenar em buffer muita entrada antes de enviá-la para o gzip, o número de pesquisas pequenas pode ser drasticamente reduzido. As opções:

  • -s e -m são para especificar o tamanho do buffer (acredito que ele esteja em KB, mas não tenho certeza)
  • -p 100 certifica-se de que os dados só são passados para o gzip quando o buffer é 100% preenchido

Executando quatro deles em paralelo, consegui obter uma taxa de transferência de 4 * 25 MB / s, como esperado.

Eu ainda me pergunto por que o gzip não permite aumentar o tamanho do buffer - desta forma, é inútil se rodar em um disco giratório.

EDIT : experimentei mais alguns comportamentos de programas de compressão:

  • bzip2 processa apenas 2 MB / s devido à sua compactação mais strong / mais intensa da CPU
  • lzop parece permitir buffers maiores: 70 MB / s por núcleo, e 2 núcleos podem maximizar meu HD sem procurar demais
por 24.05.2013 / 06:37
3
Depois de analisar as primeiras cinco palestras no MIT OpenCourseware para 6.172: "Engenharia de desempenho de sistemas de software", executei o analisador de desempenho do Linux 'perf' em um arquivo de teste moderadamente grande. O resultado parece mostrar trechos de pipeline onde uma instrução tem que esperar pelo resultado de uma anterior.

       │         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.  

A segunda última instrução é copiada para %ecx e a última tem que esperar (parar o pipeline) até que o registro %cx tenha dados prontos para uso. Esta barraca de pipeline mantém o loop contendo.

Este é o resultado de um estilo de programação C 'old-school' realmente obscuro.

    
por 30.08.2014 / 15:46
1

Uma dica que pode levá-lo a outro nível de velocidade em um processador multi-core / hyperthreading:
(supondo o Ubuntu)

sudo apt-get install moreutils

moreutils contém, entre outras coisas, "gnu parallel" - que tem muitas opções para ajudar a usar mais a sua CPU.

    
por 30.08.2014 / 15:52