[script shell] grep -f sur de grandes quanités de données

grep -f sur de grandes quanités de données [script shell] - Shell/Batch - Programmation

Marsh Posté le 02-10-2006 à 10:22:49    

Bonjour,  
 
je dois faire une recherche de la façon suivante  
:  
 
un fichier A contient 120 000 valeurs a tester qui  
sont des numéros.  
Un fichier B contient 10 000 000 de lignes qui  
commencent par des numéros qui sont  
potentiellement dans le fichier A  
 
Ce que je fais aujourd'hui :  
 
grep -f A B > resultats.  
 
Le temps de traitement est excessivement long (5  
jours) et je cherche à développer un programme  
qui me permette de résoudre le problème le plus  
rapidement possible (4 heures maximum).  
 
J'ai déjà effectué un tri sur les fichiers et mis, das mon fichier de valeur a tester, le critère ^(commence par).
 
Je me demandais s'il n'existait pas d'algorithme procedant par division de fichiers en sous fichiers et en parallelisant la recherche  
A=A1 +A2+...Ak  
 
B=B1+B2+....BN  
 
 
 
 
Une idée?  
 
Merci

Reply

Marsh Posté le 02-10-2006 à 10:22:49   

Reply

Marsh Posté le 02-10-2006 à 10:46:36    

J'ai pas de gros sets de donnée, mais essaye ca :

Code :
  1. #!/usr/bin/env python
  2. import bisect
  3.  
  4. mask = []
  5.  
  6. for line in file('A'):
  7.     bisect.insort(mask, int(line))
  8.  
  9. for line in file('B'):
  10.     key = int(line.split(' ', 1)[0])
  11.     if mask[bisect.bisect_left(mask, key)] == key:
  12.         print line,


python est pas franchement rapide, mais l'algo utilisé trie les valeur dans le fichier A puis utilise une dichotomie donc ca sera ptêtre plus rapide.
 
Sinon l'idée est reproductible en C, et la ce sera plus surement plus rapide que grep.


Message édité par 0x90 le 02-10-2006 à 10:47:04
Reply

Marsh Posté le 02-10-2006 à 13:53:26    

nandao a écrit :

Bonjour,  
 
je dois faire une recherche de la façon suivante  
:  
 
un fichier A contient 120 000 valeurs a tester qui  
sont des numéros.  
Un fichier B contient 10 000 000 de lignes qui  
commencent par des numéros qui sont  
potentiellement dans le fichier A  
 
Ce que je fais aujourd'hui :  
 
grep -f A B > resultats.  
 
Le temps de traitement est excessivement long (5  
jours) et je cherche à développer un programme  
qui me permette de résoudre le problème le plus  
rapidement possible (4 heures maximum).  
 
J'ai déjà effectué un tri sur les fichiers et mis, das mon fichier de valeur a tester, le critère ^(commence par).
 
Je me demandais s'il n'existait pas d'algorithme procedant par division de fichiers en sous fichiers et en parallelisant la recherche  
A=A1 +A2+...Ak  
 
B=B1+B2+....BN  
 
 
 
 
Une idée?  
 
Merci


Va voir chez lea-linux si j'y suis [:ddr555]


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 02-10-2006 à 18:05:56    

Voici un petit script awk effectue le travail.
Il est dans le même esprit que le programme python que propose 0x90.
 
Il présuppose que le numéro dans le fichier B est suivi d'un espace (ou d'une tabulation).

awk 'NR==FNR { Num[$0] ; next } ($1 in Num)' A B


Si le séparateur dans le fichier B est autre chose qu'un blanc :

awk -v FS='separateur' 'NR==FNR { Num[$0] ; next } ($1 in Num)' A B


Autre cas, si le numéro est de longueur fixe et qu'il n'y a pas de séparateur dans B :

awk 'NR==FNR { Num[$0] ; next } (substr($0,1,longueur_numero)  in Num)' A B


 
Je n'ai aucune idée du temps que cela va prendre !
 
Jean-Pierre.


---------------
Jean Pierre.
Reply

Marsh Posté le 02-10-2006 à 18:09:53    

Euh, je connais pas awk, mais t'es sur d'utiliser un algo dichotomique la ?

Reply

Marsh Posté le 02-10-2006 à 21:20:31    

Tu ne veux garder que les lignes du fichier B qui commencent par un numero qui est dans le fichier A, c'est ca ? A ta place je ferais ca en Perl (ou Python, ou Ruby, ou tout autre langage de script). Dans un premier temps tu lis le fichier A ligne par ligne, et tu met toutes les valeurs dans un hash (les valeurs que tu lis sont les clees du hash). Puis tu lis le fichier B ligne par ligne, et a chaque fois tu regardes si le premier champs est dans le hash. Le hash c'est a mon avis la solution la plus rapide (et aussi la plus rapide a coder). Beaucoup plus rapide en tout ca qu'une recherche "a la main" par dichotomie.

Reply

Marsh Posté le 02-10-2006 à 21:23:59    

Je suis d'accord, c'est un cas où le shell, puissant et très pratique par ailleurs, montre ici ses limites : il n'est pas fait pour traiter de telles quantités de données (5j  :ouch: ).
 
PERL me semble être une bonne solution, surtout qu'il y a de bonne chance qu'il soit déjà installé. La solution de matafan me semble bien.
Celle de 0x90 en Python semble chouette aussi.


Message édité par Elmoricq le 02-10-2006 à 21:27:48
Reply

Marsh Posté le 03-10-2006 à 22:04:53    

matafan a écrit :

Tu ne veux garder que les lignes du fichier B qui commencent par un numero qui est dans le fichier A, c'est ca ? A ta place je ferais ca en Perl (ou Python, ou Ruby, ou tout autre langage de script). Dans un premier temps tu lis le fichier A ligne par ligne, et tu met toutes les valeurs dans un hash (les valeurs que tu lis sont les clees du hash). Puis tu lis le fichier B ligne par ligne, et a chaque fois tu regardes si le premier champs est dans le hash. Le hash c'est a mon avis la solution la plus rapide (et aussi la plus rapide a coder). Beaucoup plus rapide en tout ca qu'une recherche "a la main" par dichotomie.


 
avec 120 000 valeurs dans un seul hash, j'ai eu peur de me retrouver avec des buckets extrachargés et donc de ramer pour rechercher dedans. ( j'ai eu la flemme de vérifier si python 1) resize les hash 2) trie les buckets, s'il le fait efficacement, alors ca peut être mieux avec un hash ).
 
Sinon y'a toujours des solutions sympa en C en générant un ptit automate en lisant le fichier A....

Reply

Marsh Posté le 04-10-2006 à 00:01:31    

Perl n'a aucun probleme a gerer efficacement des hashs de plusieurs centaines de miliers de clees. C'est fait pour ca. D'ailleurs j'ai fait un petit test. Je genere un fichier a.txt qui contient 120,000 valeurs aleatoire entre 0 et 1,000,000,000, et un fichier b qui contient 10,000,000 lignes commencant par une valeur aleatoire egalement entre 0 et 1,000,000,000 :
 

nicolas@austin ~/tmp $ perl -e 'foreach (1 .. 120000) { print int(rand(1000000000)) . "\n" }' > a.txt
nicolas@austin ~/tmp $ perl -e 'foreach (1 .. 10000000) { print int(rand(1000000000)) . " blah blah blah\n" }' > b.txt


 
Puis je fait tourner ce petit script, qui fait ce que nandao demande :
 

nicolas@austin ~/tmp $ cat filter.pl  
#!/usr/bin/perl -w
 
use strict;
 
my %a;
 
open(A, $ARGV[0]) or die $!;
while (<A> ) {
        chomp;
        $a{$_} = 1;
}
close(A);
 
open(B, $ARGV[1]) or die $!;
while (<B> ) {
        my @tok = split(/\s+/);
        next if @tok == 0;
        chomp($tok[0]);
        print if $a{$tok[0]};
}
close(B);


 
Les resultats :
 

nicolas@austin ~/tmp $ time ./filter.pl a.txt b.txt > out
 
real    1m3.184s
user    1m1.247s
sys     0m0.332s


 
1 minute... Legerement plus rapide que les 5 jours de nandao  ;)

Message cité 1 fois
Message édité par matafan le 04-10-2006 à 20:35:33
Reply

Marsh Posté le 04-10-2006 à 08:56:57    

matafan a écrit :

Perl n'a aucun probleme a gerer efficacement des hashs de plusieurs centaines de miliers de clees. C'est fait pour ca. D'ailleurs j'ai fait un petit test.1 minute... Legerement plus rapide que les 5 jours de nandao  ;)


 
Joli !!!
Est-ce que Python ferait aussi bien ???


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 04-10-2006 à 08:56:57   

Reply

Marsh Posté le 04-10-2006 à 10:47:07    

LC_ALL=C grep -f A B > resultats
 
ça fait souvent des étincelles.

Reply

Marsh Posté le 04-10-2006 à 14:52:55    

J'ai testé avec la même quantité de donné (après avoir corrigé un ptit bug [:cupra] ) :
real    1m10.963s
user    1m5.109s
sys     0m0.981s
Je le refais en plus simple (en faisant confiance à la hashtable :D) :
real    0m28.883s
user    0m28.203s
sys     0m0.324s
 
Moralité, Je suis une grosse loutre :D
 
[edit] Simplification encore :
real    0m15.995s
user    0m15.835s
sys     0m0.155s


Message édité par 0x90 le 04-10-2006 à 15:02:25
Reply

Marsh Posté le 04-10-2006 à 16:15:16    

J'ai testé les 3 méthodes awk, perl et grep sur mon système AIX avec le même volume de données (je ne dispose pas de python).
Voici les résultats :

awk  
real  1m14,81s  
user  1m7,86s
sys   0m2,27s
 
perl
real  3m17,72s
user  3m13,52s
sys   0m2,10s
 
grep
out of memory


A priori mon serveur semble à la traine par rapport aux votres (durée perl trois fois plus importante).
Ma commande grep s'est plantée au bout de deux secondes avec le message 'out of memory'
Pouvez vous tester avec awk et m'indiquer les temps obtenus.
 
Jean-Pierre.


---------------
Jean Pierre.
Reply

Marsh Posté le 04-10-2006 à 16:53:44    

Quoiqu'il en soit je ne vois pas comment obtenir les 5j de traitement, même awk est plus rapide, c'est pourtant pas un foudre de guerre [:pingouino]

Reply

Marsh Posté le 06-10-2006 à 21:56:47    

Elmoricq a écrit :

Quoiqu'il en soit je ne vois pas comment obtenir les 5j de traitement[:pingouino]


Ptet que les lignes du fichier "B" sont très longues... :??:  


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 07-10-2006 à 21:26:00    

0x90 a écrit :

Euh, je connais pas awk, mais t'es sur d'utiliser un algo dichotomique la ?


En effet il ne s'agit pas d'un algo dichotomique.
Ceci étant, la structure des tableaux en awk est telle que cela revient au même à mes yeux.  
Les tableaux sont gérés sous forme d'arbre èquilibré, toutes les feuilles sont à la même profondeur dans l'arbre (à un niveau prés) et donc accessibles avec un nombre équivalent d'opérations.  
Une autre incidence de cette structure est que l'ordre des indices récupéré par uneboucle for in est imprédictible.
 
 


---------------
Jean Pierre.
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed