Pourquoi gzip est-il lent alors que les performances du CPU et du disque dur ne sont pas maximisées?

14

J'ai quelques fichiers JSON, 20 Go chacun, avec lesquels je veux compresser gzip:

gzip file1.json

Cela prend un cœur de processeur complet, très bien.

Il traite environ 25 Mo / s (enregistré atop), mon disque dur peut lire 125 Mo / s et j'ai 3 cœurs de processeur gratuits, donc je m'attends à obtenir une accélération lors de la compression de plusieurs fichiers en parallèle. Je cours donc dans d'autres terminaux:

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

Étonnamment, mon débit n'augmente pas; Le processeur est d'environ 25% sur chaque cœur, et mon HD ne lit toujours que 25 Mo / s.

Pourquoi et comment y remédier?

nh2
la source

Réponses:

17

Je l'ai découvert:

La raison en est que gzip(en termes de vitesse du processeur vs vitesse de recherche HD de nos jours) fonctionne des tailles de tampon extrêmement faibles .

Il lit quelques Ko du fichier d'entrée, le compresse et le vide dans le fichier de sortie. Étant donné que cela nécessite une recherche de disque dur, seules quelques opérations peuvent être effectuées par seconde.

La raison pour laquelle ma performance n'a pas évolué, c'est que déjà on gzipcherchait comme un fou.


J'ai travaillé autour de cela en utilisant l' bufferutilitaire unix :

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

En mettant en mémoire tampon beaucoup d'entrées avant de les envoyer à gzip, le nombre de petites recherches peut être considérablement réduit. Les options:

  • -set -mdoivent spécifier la taille du tampon (je pense que c'est en Ko, mais je ne suis pas sûr)
  • -p 100 s'assure que les données ne sont transmises à gzip qu'une fois le tampon rempli à 100%

En exécutant quatre d'entre eux en parallèle, je pouvais obtenir un débit de 4 * 25 Mo / s, comme prévu.


Je me demande toujours pourquoi gzip ne permet pas d'augmenter la taille du tampon - de cette façon, il est assez inutile s'il est exécuté sur un disque en rotation.

EDIT : J'ai essayé quelques comportements de programmes de compression supplémentaires:

  • bzip2 ne traite que 2 Mo / s en raison de sa compression plus forte / plus intensive en CPU
  • lzop semble autoriser des tampons plus importants: 70 Mo / s par cœur et 2 cœurs peuvent maximiser ma HD sans sur-rechercher
nh2
la source
Peut ddfaire la même chose?
Simon Kuang
@SimonKuang Je soupçonne que cela ddpeut faire la même chose avec son bs=option, oui.
nh2
Cela ressemble à une coïncidence intéressante: pour un seul fichier, la taille du bloc s'est avérée utiliser pleinement à la fois un seul cœur de processeur et les IOPS d'un lecteur.
Dave L.
3

Après avoir regardé les cinq premières conférences du MIT OpenCourseware pour 6.172: "Performance Engineering of Software Systems", j'ai exécuté l'analyseur de performances Linux "perf" sur un fichier de test modérément volumineux. Le résultat semble montrer les arrêts de pipeline où une instruction doit attendre le résultat d'une précédente.

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

L'avant-dernière instruction est en cours de copie %ecxet la dernière doit attendre (blocage du pipeline) jusqu'à ce que le %cxregistre ait des données prêtes à être utilisées. Ce blocage de pipeline retient la boucle conteneur.

Ceci est le résultat d'un style de programmation C «old-school» vraiment obscur.

user1295785
la source
1

Une astuce qui pourrait l'amener à un autre niveau de vitesse sur un processeur multicœur / hyperthreading:
(en supposant Ubuntu)

sudo apt-get install moreutils

moreutils contient entre autres "gnu parallel" - qui a beaucoup d'options pour vous aider à utiliser plus de votre CPU.

Hannu
la source