Migliorare le prestazioni di grepping su un file enorme

10

Ho FILE_A con oltre 300.000 righe e FILE_B con oltre 30 milioni di righe. Ho creato uno script Bash che esegue il greps di ogni riga in FILE_A su in FILE_B e scrive il risultato di grep in un nuovo file.

L'intero processo richiede più di 5 ore.

Come posso migliorare le prestazioni del mio script?

Sto usando grep -F -m 1 come comando grep. FILE_A assomiglia a questo:

123456789 
123455321

e FILE_B è simile a questo:

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

Quindi con Bash ho un ciclo while che preleva la riga successiva in FILE_A e lo trascina in FILE_B. Quando il modello è trovato in FILE_B, lo scrivo nel file result.txt.

while read -r line; do
   grep -F -m1 $line 30MFile
done < 300KFile
    
posta rogerio_marcio 30.05.2012 - 00:02
fonte

5 risposte

17

Prova a usare grep --file==FILE_A . Quasi certamente carica gli schemi in memoria, il che significa che eseguirà solo una scansione di FILE_B una volta.

grep -F -m1 --file==300KFile 30MFile
    
risposta data 30.05.2012 - 00:11
fonte
2

Ecco una Perl risposta per i posteri. Lo faccio abitualmente per abbinare linee 1M a linee da 30-35M. Ci vogliono circa 10 secondi per terminare.

Innanzitutto, fai clic su 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;

Quindi, se il tuo grande file è delimitato e sai quale colonna andare dopo, controlla solo l'esistenza della chiave hash mentre esegui FILE_B, che è molto, molto più veloce del controllo di uguaglianza o corrispondenza delle espressioni regolari:

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;

Se il tuo file target più grande non è bello da analizzare, allora questo script perde il suo valore in quanto gran parte della sua velocità deriva dal non dover attivare il espressione regolare motore.

    
risposta data 05.09.2012 - 20:30
fonte
1

Se non ti dispiace programmare un po 'di più, prendi in considerazione l'utilizzo di alberi dei suffissi (o una variante).

Puoi pre-processare FILE_B usando l'algoritmo di Ukkonen in tempo lineare. Quindi, si interroga ogni riga in FILE_A nel tempo lineare in lunghezza della linea e si ottengono tutti i numeri di riga corrispondenti (potrebbe essere necessario adattare l'albero un po ') che è possibile scrivere in un file di risultati.

L'intera procedura viene eseguita nel tempo O (n + Nm) se n è la lunghezza di FILE_B , N è il numero di righe in FILE_A e m è la lunghezza della linea più lunga in FILE_A - Questo è essenzialmente runtime lineare. Batte il tempo quadratico in base al quale il tuo approccio originale ha bisogno di magnitudini.

    
risposta data 30.05.2012 - 00:38
fonte
1

Ho trovato il flag --mmap ultimamente, non ho avuto la possibilità di testarlo, ma sarò felice di conoscere le tue scoperte. Ecco la descrizione dalla pagina man:

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

Vedi questo o questo per ulteriori informazioni su mmap .

    
risposta data 30.05.2012 - 00:44
fonte
-1

Perché non metti quel file in un database  i database sono davvero bravi a fare un unione efficiente, un hash, un loop annidato come questo . E sono davvero bravi a utilizzare la memoria virtuale

    
risposta data 19.05.2014 - 04:28
fonte

Leggi altre domande sui tag