2009-10-19 7 views
5

Abbiamo un corpo maturo di codice che carica i dati da file in un database. Ci sono diversi formati di file; sono tutti campi a larghezza fissa.Come posso velocizzare l'elaborazione di dati a larghezza fissa di Perl?

Parte del codice utilizza la funzione Perl unpack() per leggere i campi dai dati di input in variabili del pacchetto. La logica aziendale è quindi in grado di riferirsi a questi campi in un modo "leggibile".

Il codice di lettura file viene generato da una descrizione di formato una volta, prima di leggere un file.

Nella forma schizzo, il codice generato assomiglia a questo:

while (<>) { 

    # Start of generated code. 

    # Here we unpack 2 fields, real code does around 200. 
    ($FIELDS::transaction_date, $FIELDS::customer_id) = unpack q{A8 A20}; 

    # Some fields have leading space removed 
    # Generated code has one line like this per affected field. 
    $FIELDS::customer_id =~ s/^\s+//; 

    # End of generated code. 

    # Then we apply business logic to the data ... 
    if ($FIELDS::transaction_date eq $today) { 
     push @fields, q{something or other}; 
    } 

    # Write to standard format for bulk load to the database. 
    print $fh join('|', @fields) . q{\n} or die; 
} 

Profiling il codice rivela che circa il 35% del tempo viene speso nella decomprimere e striscia leader-spazio. Il tempo rimanente è dedicato alla convalida e alla trasformazione dei dati e alla scrittura nel file di output.

Sembra che non ci sia una singola parte della business logic che richiede più dell'1-2% del tempo di esecuzione.

La domanda è: possiamo liberare un po 'più la velocità dal disimballaggio e dallo spogliamento dello spazio in qualche modo? Preferibilmente senza dover rifattorizzare tutto il codice che fa riferimento alle variabili del pacchetto FIELDS.

EDIT:

Nel caso in cui si fa la differenza

$ perl -v 
This is perl, v5.8.0 built for PA-RISC1.1 
+0

Sarebbe interessante sapere se l'utilizzo di un elenco di variabili del pacchetto sul lato sinistro della scompattazione è probabile che sia ottimale. –

risposta

1

Sì. Estrarre usando substr è probabilmente il modo più veloce per farlo. Ovvero:

$FIELDS::transaction_date = substr $_, 0, 8; 
$FIELDS::customer_id  = substr $_, 8, 20; 

è probabile che sia più veloce. Ora, se scrivessi questo codice a mano, non rinuncerei a unpack, ma se stai generando il codice, potresti anche dargli un colpo e misurarlo.

Vedere anche le risposte a Is Perl’s unpack() ever faster than substr()?

Quanto a nudo gli spazi iniziali, s/^\s+// è probabile che sia il metodo più veloce.

Aggiornamento: È difficile dire qualcosa di definito senza essere in grado di eseguire benchmark. Tuttavia, come su:

my $x = substr $_, 0, 8; 

per i campi che non hanno bisogno di alcun taglio e

my ($y) = substr($_, 8, 20) =~ /\A\s+(.+?)\s+\z/; 

che non hanno bisogno di tagliare?

+0

Sperimenterò e riferirò. –

+3

Una cosa da notare è che spacchettare 'A' rimuove gli spazi finali 'gratuitamente'. –

+0

Sembra promettente finora. Il benchmark di base mostra una serie di substrati per campo e le regex di trascinamento di strip sono circa il 50% più veloci per noi rispetto all'utilizzo di una decompressione. I test stanno passando, ma il mio schermo è pieno di avvertimenti Perlish, quindi non sono ancora arrivato. –

7

Ho effettivamente affrontato questo problema più e più volte. Unpack is better than substr.

Per quanto riguarda gli spazi di spogliamento, sei praticamente fregato. Quella modifica regex è il modo "ufficiale" per farlo. Potresti essere in grado di guadagnare un po 'di efficienza perfezionando le istruzioni di decompressione (supponendo che nessun dato sia più lungo di 4 cifre, perché decomprimere le 12 cifre complete del campo?), Ma in caso contrario, l'analisi è solo un p.i.t.a.

Buona fortuna con i dati flat. Frying junk legacy, come lo odio.

3

Sei sicuro di essere associato a questa attività? Il calcolo è abbastanza semplice da sospettare che l'intero processo sia probabilmente legato all'I/O. In tal caso, l'ottimizzazione per una decompressione più veloce non ti farà guadagnare molto tempo.

Nel caso in cui il processore sia effettivamente collegato, il problema descritto sembra abbastanza parallelizzabile, ma ovviamente il diavolo si trova nei dettagli del calcolo aziendale.

+0

Piuttosto sicuro, sì. Nel codice reale l'I/O e il disimballaggio sono separati, diversamente dall'esempio in vaso sopra riportato. Il parallelismo è una buona chiamata - lo facciamo già. Vorrebbe comunque trascorrere meno tempo a tagliare le stringhe però. –

+2

Per me il collo di bottiglia più grande è il database stesso; se hai a che fare con dati flat, potresti avere a che fare con un database orribile antico e piatto. Ho risolto il problema estraendo i dati a intervalli ad un database moderno, ma per i dati "in tempo reale" è quasi sempre il database a causare i rallentamenti. – Satanicpuppy

+0

Deve essere d'accordo. Nel complesso, il nostro processo sta trascorrendo la maggior parte del tempo a spulciare i dati nel database.Siamo in grado di utilizzare processi paralleli per velocizzarlo. Mi ha colpito il fatto che una parte così grande del "Perl-time" è stata spesa in una parte così piccola del codice che potrebbe esserci spazio per una facile ottimizzazione qui. –

1

Questo potrebbe essere anche qualcosa per XS - in modo da utilizzare una funzione C per modificare i dati. Potrei immaginare che questo è molto più veloce di qualsiasi altra cosa in quanto è possibile controllare manualmente quando i dati vengono realmente copiati.
Il processo di compilazione diventerà più difficile in quanto si ha una dipendenza dal compilatore C e richiederà ulteriori passaggi di integrazione.

+0

Grazie. Probabilmente per il mio progetto il guadagno non giustificherebbe la complessità extra. Come osservato da Satanicpuppy, ci sono altre parti del processo che sono più costose di questo. Potrebbe funzionare per qualcun altro con un problema simile però. –

+3

Se la maggior parte del tempo della CPU viene speso nel motore regex e in una funzione incorporata, allora la maggior parte del tempo viene speso in C land (vale a dire non su pedine e gestione di strutture dati perl). E ci vuole un buon programmatore per battere le persone che hanno scritto la perl VM. disfare i bagagli è veloce! – tsee

1

Basta farlo andare in parallelo. È banale, e su qualsiasi macchina anche lontanamente moderna sarà più veloce.

0

Il benchmark di una versione del nostro codice basata su sub ha suggerito che potrebbe essere circa il 50% più veloce rispetto al nostro disimballaggio esistente. Confrontando i codici in atto nell'applicazione effettiva, la versione substr ha permesso una riduzione del 16% del tempo di esecuzione. Questo è vicino a quello che speravamo in base al benchmark e alla profilazione cui fa riferimento la domanda.

Questa ottimizzazione potrebbe essere utile per noi. Ma, abbiamo una migrazione verso un nuovo sistema operativo all'orizzonte, quindi aspetteremo e vedremo come i codici funzionano lì prima di procedere. Abbiamo aggiunto un test per tenere d'occhio i benchmark comparativi.

Il linguaggio che abbiamo ora è:

$FIELDS::transaction_date = substr($_, 0, 8) || ''; 
$FIELDS::transaction_date =~ s/\s+\z//; 
$FIELDS::customer_id = substr($_, 8, 20) || ''; 
$FIELDS::customer_id =~ s/\s+\z//; 

Seguito da selettiva rimozione leader spazio come prima.

Grazie per tutte le risposte finora. Accetterò Sinan perché ha funzionato per noi, nonostante appaia essere "semplicemente sbagliato".

Problemi correlati