¿Por qué gzip es lento a pesar de que el rendimiento de la CPU y el disco duro no se maximiza?

14

Tengo algunos archivos JSON, de 20 GB cada uno, con los que quiero comprimir gzip:

gzip file1.json

Esto ocupa un núcleo de CPU completo, todo bien.

Procesa alrededor de 25 MB / s (registrado atop), mi disco duro puede leer 125 MB / sy tengo 3 núcleos de procesador libres, por lo que espero acelerar al comprimir varios archivos en paralelo. Entonces corro en otras terminales:

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

Sorprendentemente, mi rendimiento no aumenta; La CPU es de alrededor del 25% en cada núcleo, y mi HD todavía solo lee a 25 MB / s.

¿Por qué y cómo abordarlo?

nh2
fuente

Respuestas:

17

Lo descubrí:

La razón es que gzipopera en (en términos de velocidad de CPU frente a velocidad de búsqueda HD en estos días) tamaños de búfer extremadamente bajos .

Lee unos pocos KB del archivo de entrada, lo comprime y lo pasa al archivo de salida. Dado que esto requiere una búsqueda de disco duro, solo se pueden realizar algunas operaciones por segundo.

La razón por la que mi rendimiento no se amplió es porque ya uno gzipestaba buscando como loco.


Trabajé alrededor de esto usando la bufferutilidad Unix :

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

Al almacenar una gran cantidad de entrada antes de enviarla a gzip, el número de búsquedas pequeñas se puede reducir drásticamente. Las opciones:

  • -sy -mdeben especificar el tamaño del búfer ( creo que está en KB, pero no estoy seguro)
  • -p 100 se asegura de que los datos solo se pasen a gzip una vez que el búfer esté lleno al 100%

Al ejecutar cuatro de estos en paralelo, podría obtener un rendimiento de 4 * 25 MB / s, como se esperaba.


Todavía me pregunto por qué gzip no permite aumentar el tamaño del búfer; de esta manera, es bastante inútil si se ejecuta en un disco giratorio.

EDITAR : Probé algunos comportamientos más de programas de compresión:

  • bzip2 solo procesa 2 MB / s debido a su compresión más fuerte / más intensiva de CPU
  • lzop parece permitir búferes más grandes: 70 MB / s por núcleo, y 2 núcleos pueden maximizar mi HD sin buscar demasiado
nh2
fuente
Puede ddhacer lo mismo?
Simon Kuang
@SimonKuang Sospecho que ddpuede hacer lo mismo con su bs=opción, sí.
nh2
Parece una coincidencia interesante que, para un solo archivo, el tamaño del bloque utilizó completamente un solo núcleo de CPU y las IOPS de una unidad.
Dave L.
3

Después de mirar las primeras cinco conferencias en el OpenCourseware de MIT para 6.172: "Ingeniería de rendimiento de sistemas de software", ejecuté el analizador de rendimiento de Linux 'perf' en un archivo de prueba moderadamente grande. El resultado parece mostrar paradas de canalización donde una instrucción tiene que esperar el resultado de una 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.  

La segunda última instrucción se está copiando %ecxy la última tiene que esperar (detener la canalización) hasta que el %cxregistro tenga datos listos para usar. Esta parada de tubería sostiene el bucle contenedor.

Este es el resultado de un estilo de programación en C 'oscuro' realmente oscuro.

usuario1295785
fuente
1

Un consejo que podría llevarlo a otro nivel de velocidad en una CPU multi-core / hyperthreading:
(suponiendo Ubuntu)

sudo apt-get install moreutils

moreutils contiene, entre otras cosas, "gnu parallel", que tiene muchas opciones para ayudar a usar más de su CPU.

Hannu
fuente