Comment utiliser GNU Parallel efficacement

8

Supposons que je veuille trouver toutes les correspondances dans un fichier texte compressé:

$ gzcat file.txt.gz | pv --rate -i 5 | grep some-pattern

pv --rateutilisé ici pour mesurer le débit des tuyaux. Sur ma machine, c'est environ 420Mb / s (après décompression).

Maintenant j'essaye de faire du grep parallèle en utilisant GNU parallel.

$ gzcat documents.json.gz | pv --rate -i 5 | parallel --pipe -j4 --round-robin grep some-pattern

Le débit est maintenant abaissé à ~ 260 Mo / s. Et ce qui est le plus intéressant parallel, c'est d'utiliser beaucoup de CPU. Plus que des grepprocessus (mais moins que gzcat).

EDIT 1 : J'ai essayé différentes tailles de blocs ( --block), ainsi que différentes valeurs pour -N/ -Loptions. Rien ne m'aide à ce stade.

Qu'est-ce que je fais mal?

Denis Bazhenov
la source

Réponses:

9

Je suis vraiment surpris que vous obteniez 270 Mo / s en utilisant GNU Parallel's --pipe. Mes tests atteignent généralement un maximum d'environ 100 Mo / s.

Votre goulot d'étranglement est très probablement dans GNU Parallel: --pipen'est pas très efficace. --pipepart, cependant, est: Ici, je peux obtenir de l'ordre de 1 Go / s par cœur de processeur.

Malheureusement, il existe quelques limitations à l'utilisation --pipepart:

  • Le fichier doit être recherché (c'est-à-dire sans pipe)
  • Vous devez pouvoir trouver le début d'un enregistrement avec --recstart / - recend (c'est-à-dire pas de fichier compressé)
  • Le numéro de ligne est inconnu (vous ne pouvez donc pas avoir un enregistrement de 4 lignes).

Exemple:

parallel --pipepart -a bigfile --block 100M grep somepattern
Ole Tange
la source
1
Merci. Y a-t-il une raison pour laquelle --pipeest inefficace? Je veux dire que c'est une sorte de problème fondamental ou plus de mise en œuvre spécifique.
Denis Bazhenov
2
Oui: GNU Parallel est écrit en perl, et avec --pipechaque octet unique doit passer par le processus unique, qui doit faire un peu de traitement sur chaque octet. Avec la --pipepartplupart des octets ne sont jamais vus par le processus central: ils sont traités par des travaux générés. Comme ce sont assez peu de lignes qui constituent le goulot d'étranglement --pipe, j'accueillerais un codeur C / C ++ qui réécrirait la partie qui serait ensuite exécutée pour les personnes ayant un compilateur C sur leur chemin.
Ole Tange
2

grep est très efficace - cela n'a aucun sens de l'exécuter en parallèle. Dans votre commande, seule la décompression a besoin de plus de cpu, mais cela ne peut pas être mis en parallèle.

Le fractionnement des entrées par parallèle nécessite plus de CPU que d'obtenir des lignes correspondantes par grep.

Changement de situation si vous souhaitez utiliser au lieu de grep quelque chose qui nécessite beaucoup plus de cpu pour chaque ligne - alors parallèle aurait plus de sens.

Si vous souhaitez accélérer cette opération - regardez où se trouvent les goulots d'étranglement - c'est probablement la décompression (puis aide à utiliser un autre outil de décompression ou un meilleur processeur) ou - la lecture à partir du disque (puis aide à utiliser un autre outil de décompression ou un meilleur système de disque).

D'après mon expérience - il est parfois préférable d'utiliser lzma (-2 par exemple) pour compresser / décompresser des fichiers - il a une compression plus élevée que gzip, donc beaucoup moins de données doivent être lues sur le disque et la vitesse est comparable.

indéfinir
la source
1
En effet, c'est mon cas. Un processus Java très gourmand en CPU est utilisé à la place de grep. J'ai simplifié un peu la question. Et encore, manger en parallèle beaucoup de CPU ne fournit pas beaucoup de travail aux processus Java.
Denis Bazhenov
1

La décompression est ici le goulot d'étranglement. Si la décompression n'est pas parallélisée en interne, vous n'y arriverez pas par vous-même. Si vous avez plus d'un travail comme celui-là, alors bien sûr, lancez-les en parallèle, mais votre pipeline en lui-même est difficile à paralléliser. La division d'un flux en flux parallèles n'en vaut presque jamais la peine et peut être très pénible avec la synchronisation et la fusion. Parfois, vous devez simplement accepter que plusieurs cœurs ne vous aideront pas pour chaque tâche que vous exécutez.

En général, la parallélisation en shell doit se faire principalement au niveau de processus indépendants.

orion
la source
1
Il ne semble pas que la décompression soit un goulot d'étranglement en cas d'utilisation parallel. Je suis d'accord que c'est certainement dans le premier cas (sans parallèle), mais dans le second (avec parallèle) le goulot d'étranglement est du côté parallèle. Cela découle de l'observation que le débit diminue de manière significative, tel que mesuré par pv. Si le goulot d'étranglement est en décompression, le débit ne changera pas ce que vous ajoutez au pipeline. C'est une définition très intuitive du débit, je suppose - la chose qui limite le plus le débit.
Denis Bazhenov
1
Il est possible que grep soit si rapide, qu'il finisse plus vite que ce qui parallelpeut être écrit sur son pipe. Dans ce cas, la plupart des grepprocessus attendent simplement d'en avoir plus, tout en paralleltravaillant 24 heures sur 24 pour multiplexer les blocs en plusieurs canaux (qui sont des opérations d'E / S supplémentaires et peuvent même bloquer la décompression si le tampon est plein). Avez-vous également essayé de jouer avec le --blockparamètre? Par défaut, 1Mjusqu'à ce qu'un grep obtienne des 1Mdonnées, le reste est presque certainement déjà terminé. Nous revenons donc au fait que cela n'a aucun sens de paralléliser cela.
orion
1
Oui, j'ai essayé ces options avec des blocs de grande et petite taille. Ainsi que différentes valeurs pour -N/ -Loptions. Il semble que les options par défaut soient très proches de l'optimum local que j'ai connu :)
Denis Bazhenov
1
Essayez de le chronométrer avec et sans pv(avec time). De cette façon, vous pouvez voir si pvlui-même le ralentit. Si c'est le cas, la parallelcopie de données dans des tuyaux est certainement un surcoût supplémentaire. Et dans tous les cas, je suis sûr que grepc'est presque en temps réel dans ce cas, surtout si le modèle est une chaîne simple sans beaucoup de retour en arrière. De plus, parallelentrelacera et gâchera les grepsorties.
orion
1
Je vais vérifier que pvlui - même ne cause pas le problème, merci pour les conseils.
Denis Bazhenov