Amélioration des performances pour la récupération d'un fichier volumineux


10

J'ai FILE_A qui a plus de 300 000 lignes et FILE_B qui a plus de 30 millions de lignes. J'ai créé un script Bash qui greps chaque ligne dans FILE_A dans FILE_B et écrit le résultat de la grep dans un nouveau fichier.

L'ensemble de ce processus prend plus de 5 heures.

Comment puis-je améliorer les performances de mon script?

J'utilise grep -F -m 1comme commande grep. FILE_A ressemble à ceci:

123456789 
123455321

et FILE_B est comme ceci:

123456789,123456789,730025400149993,
123455321,123455321,730025400126097,

Donc, avec Bash, j'ai une whileboucle qui sélectionne la ligne suivante dans FILE_A et la récupère dans FILE_B. Lorsque le motif se trouve dans FILE_B, je l'écris dans le fichier result.txt.

while read -r line; do
   grep -F -m1 $line 30MFile
done < 300KFile

Réponses:


17

Essayez d'utiliser grep --file==FILE_A. Il charge presque certainement les modèles en mémoire, ce qui signifie qu'il n'analysera FILE_B qu'une seule fois.

grep -F -m1 --file==300KFile 30MFile

Cela ne fonctionnerait que si je dispose de suffisamment de mémoire, n'est-ce pas?
rogerio_marcio

Honnêtement, je ne l'ai pas essayé moi-même sur des fichiers de cette taille, mais je pense que cela devrait considérablement améliorer votre vitesse. Si vous êtes sur une machine moderne, vous ne devriez avoir aucun problème à conserver un fichier 300K en mémoire. (Ou un 30M d'ailleurs).
Gort the Robot

quand j'ai utilisé l'option -f (--file), il a essentiellement recréé le 30MFile. Est-ce que je fais quelque chose de mal?
rogerio_marcio

Hmmm ... peut-être que le 300Kfile avait une ligne vierge dedans?
Gort le robot

juste sur place! c'était ça! ça marchait parfaitement, ça s'est terminé en 30 secondes! Merci!!
rogerio_marcio

2

Voici une réponse Perl pour la postérité. Je fais régulièrement cela pour faire correspondre les lignes 1M aux lignes 30-35M. Il faut environ 10 secondes pour terminer.

Tout d'abord, hachez FILE_A:

my %simple_hash;
open my $first_file, '<', 'FILE_A' or die "What have you done?! $!";
while (<$first_file>) {
  chomp;                 ## Watch out for Windows newlines
  $simple_hash{$_} = 1;  ## There may be an even faster way to define this
}
close $first_file;

Ensuite, si votre gros fichier est délimité et savez quelle colonne aller après, vérifiez juste l'existence de la clé de hachage lorsque vous exécutez FILE_B, ce qui est beaucoup, beaucoup plus rapide que de vérifier l'égalité ou la correspondance des expressions régulières:

open my $second_file, '<', 'FILE_B' or die "Oh no, not again.. $!";
while (<$second_file>) {
  my ($col1, undef) = split ',';
  if (exists($simple_hash{$col1}) {
    print $_;
  }
}
close $second_file;

Si votre fichier cible plus volumineux n'est pas bien analysable, alors ce script perd sa valeur car une grande partie de sa vitesse vient du fait de ne pas avoir à lancer le moteur d' expression régulière .


1

Si cela ne vous dérange pas d'une programmation plus complexe, envisagez d'utiliser des arborescences de suffixes (ou une variante).

Vous pouvez prétraiter en FILE_Butilisant l'algorithme d'Ukkonen en temps linéaire. Ensuite, vous interrogez chaque ligne en FILE_Atemps linéaire en longueur de ligne et obtenez tous les numéros de ligne qui correspondent (peut-être besoin d'adapter l'arbre un peu) que vous pouvez écrire dans un fichier de résultats.

L'ensemble de la procédure s'exécute dans le temps O (n + Nm) si n est la longueur de FILE_B, Nest le nombre de lignes dans FILE_Aet m est la longueur de la ligne la plus longue FILE_A- c'est essentiellement un temps d'exécution linéaire. Bat le temps quadratique dont votre approche originale a besoin par ampleur.


1

J'ai trouvé le --mmapdrapeau récemment, je n'ai pas eu l'occasion de le tester, mais je serai heureux d'entendre vos conclusions. Voici la description de la page de manuel:

--mmap If  possible, use the mmap(2) system call to read input, instead
      of the default read(2) system call.  In some situations,  --mmap
      yields  better performance.  However, --mmap can cause undefined
      behavior (including core dumps) if an input file  shrinks  while
      grep is operating, or if an I/O error occurs.

Voir ceci ou cela pour plus d'informations sur mmap.


Je vais certainement essayer ceci et je vous ferai savoir comment ça se passe. Quelle est la probabilité que je rencontre un vidage de mémoire?
rogerio_marcio

@rogerio_marcio Eh bien, si je comprends bien l'homme, "si le fichier rétrécit pendant que grep fonctionne, ou si une erreur d'E / S se produit.". Pas vraiment probablement, mais vous devriez mieux le savoir. (Si comme je suppose que le fichier est intact pendant la grep - cela ne devrait pas se produire)
Ramzi Kahil

Pour tester cette --mmapdose sans rien vider, je recommanderais une course avec --mmapet une sans. Et puis utilisez wcpour voir que vous avez la même quantité de sortie - cela devrait être un test robuste étant donné que nous avons exécuté 2 fois grep, et juste un indicateur différait.
Ramzi Kahil

@rogerio_marcio Avez-vous essayé cela? Des idées?
Ramzi Kahil

-1

pourquoi ne pas mettre ce fichier dans une base de données, les bases de données sont vraiment bonnes pour faire une fusion efficace, un hachage, une jointure en boucle imbriquée comme ceci. Et ils sont vraiment bons pour utiliser la mémoire virtuelle


Tout ce que vous faites avec toutes les autres réponses est de réinventer la roue de la base de données
Andyz Smith
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.