Miglioramento delle prestazioni per il grepping su un file di grandi dimensioni


10

Ho FILE_A che ha oltre 300.000 linee e FILE_B che ha oltre 30 milioni di linee. Ho creato uno script Bash che inserisce ciascuna riga in FILE_A in FILE_B e scrive il risultato del grep in un nuovo file.

L'intero processo richiede oltre 5 ore.

Come posso migliorare le prestazioni della mia sceneggiatura?

Sto usando grep -F -m 1come comando grep. FILE_A è simile al seguente:

123456789 
123455321

e FILE_B è così:

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

Quindi con Bash ho un whileloop che seleziona la riga successiva in FILE_A e la inserisce in FILE_B. Quando lo schema si trova in FILE_B, lo scrivo nel file result.txt.

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

Risposte:


17

Prova a usare grep --file==FILE_A. Carica quasi sicuramente i motivi in ​​memoria, il che significa che scansionerà FILE_B solo una volta.

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

Funzionerebbe solo supponendo che abbia abbastanza memoria, giusto?
rogerio_marcio,

Onestamente, non l'ho provato io stesso su file di quelle dimensioni, ma credo che dovrebbe migliorare notevolmente la tua velocità. Se utilizzi una macchina moderna, non dovresti avere problemi a conservare un file da 300 KB in memoria. (O 30 M per quello.)
Gort il Robot

quando ho usato l'opzione -f (--file), ho praticamente ricreato il file 30M. Sto facendo qualcosa di sbagliato?
rogerio_marcio,

Hmmm ... forse il file 300K aveva una riga vuota?
Gort il robot

proprio sul posto! era quello! ha funzionato perfettamente, è finito in 30 secondi! grazie!!
rogerio_marcio,

2

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

Innanzitutto, esegui l'hash di 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 file di grandi dimensioni è delimitato e sai quale colonna seguire, controlla solo l'esistenza della chiave hash mentre esegui FILE_B, che è molto, molto più veloce del controllo dell'uguaglianza o della 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 di destinazione più grande non è ben analizzabile, allora questo script perde il suo valore poiché gran parte della sua velocità deriva dal non dover accendere il motore delle espressioni regolari .


1

Se non ti dispiace un po 'più di programmazione coinvolta, considera l'utilizzo di alberi di suffisso (o una variante).

Puoi preelaborare FILE_Busando l'algoritmo di Ukkonen in tempo lineare. Quindi, esegui una query su ogni riga nel FILE_Atempo lineare nella lunghezza della riga e ottieni tutti i numeri di riga corrispondenti (potrebbe essere necessario adattare l'albero un po ') che puoi 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 linee in FILE_Ae m è la lunghezza della linea più lunga in FILE_A- questo è essenzialmente tempo di esecuzione lineare. Batte il tempo quadratico di cui il tuo approccio originale ha bisogno per grandezza.


1

Recentemente ho trovato la --mmapbandiera, non ho avuto la possibilità di provarla, ma sarò felice di sapere quali sono 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.


Ci proverò sicuramente e ti farò sapere come va. Quanto è probabile che incontrerò una discarica principale?
rogerio_marcio,

@rogerio_marcio Bene, a quanto ho capito, "se il file si restringe mentre grep è in funzione o se si verifica un errore I / O.". Non molto probabilmente, ma dovresti saperlo meglio. (Se suppongo che il file non sia toccato mentre grep - questo non dovrebbe accadere)
Ramzi Kahil,

Per testare quella --mmapdose senza scaricare nulla, consiglierei una corsa con --mmape una senza. E poi usa wcper vedere che hai la stessa quantità di output - questo dovrebbe essere un test robusto considerando che abbiamo eseguito 2 volte grep, e solo una bandiera differiva.
Ramzi Kahil,

@rogerio_marcio Hai provato questo? Qualche intuizione?
Ramzi Kahil,

-1

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


Tutto quello che stai facendo con tutte le altre risposte è reinventare la ruota del database
Andyz Smith,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.