2009-10-19 1 views
5

Wir haben einen ausgereiften Code, der Daten aus Dateien in eine Datenbank lädt. Es gibt verschiedene Dateiformate; Sie sind alle Felder mit fester Breite.Wie kann ich Perls Verarbeitung von Daten mit fester Breite beschleunigen?

Ein Teil des Codes verwendet die Perl-Funktion unpack(), um Felder aus den Eingabedaten in Paketvariablen zu lesen. Business-Logik ist dann in der Lage, auf diese Felder in einer "menschenlesbaren" Weise zu verweisen.

Der Dateilesecode wird vor dem Lesen einer Datei aus einer Formatbeschreibung generiert.

In skizzenhaft sucht der erzeugte Code wie folgt:

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; 
} 

den Code Profilierung ergibt, dass etwa 35% der Zeit in dem Entpack ausgegeben wird, und führt Raumstreifen. Die verbleibende Zeit wird für die Überprüfung und Umwandlung der Daten und das Schreiben in die Ausgabedatei aufgewendet.

Es scheint, dass es keinen einzelnen Teil der Geschäftslogik gibt, der mehr als 1-2% der Laufzeit beansprucht.

Die Frage ist - können wir etwas mehr Geschwindigkeit aus dem Auspacken und Weltraum-Strippen irgendwie herauskitzeln? Vorzugsweise ohne den ganzen Code umzuformen, der auf die FIELDS-Paketvariablen verweist.

EDIT:

Falls es einen Unterschied

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

Wäre interessant zu wissen, ob die Verwendung einer Liste von Paketvariablen auf der linken Seite des Entpackens wahrscheinlich optimal ist. –

Antwort

1

macht Ja. Extrahieren mit substr ist wahrscheinlich der schnellste Weg, es zu tun. Das heißt:

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

wird wahrscheinlich schneller sein. Nun, wenn ich diesen Code handschriftlich schreiben würde, würde ich nicht auf unpack verzichten, aber wenn Sie den Code generieren, können Sie es genauso gut ausprobieren.

Siehe auch die Antworten auf Is Perl’s unpack() ever faster than substr()?

Wie für führende Leerzeichen Strippen ist s/^\s+// wahrscheinlich die schnellste Methode sein.

Update: Es ist schwer, etwas definitiv zu sagen, ohne Benchmarks ausführen zu können. Doch wie etwa:

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

für Felder, die das Zuschneiden nicht brauchen und

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

das Trimmen brauchen?

+0

Ich werde experimentieren und Bericht erstatten. –

+3

Eine Sache zu beachten ist, dass "A" entpacken Streifen Leerzeichen "kostenlos" entpacken. –

+0

Sieht so vielversprechend aus. Das grundlegende Benchmarking zeigt eine Reihe von Feld-Subtrats und Strip-Trailing-Regexes ist für uns um 50% schneller als die Verwendung eines Entpackens. Tests sind vorüber, aber mein Bildschirm ist voller Perlish-Warnungen, also bin ich noch nicht ganz da. –

7

Ich habe dieses Problem immer und immer wieder behandelt. Unpack is better than substr.

Soweit Abstreifen Räume geht, bist du ziemlich geschraubt. Dieser Regex-Hack ist der "offizielle" Weg, dies zu tun. Sie können vielleicht etwas effizienter werden, wenn Sie Ihre Entpackungsanweisungen verfeinern (unter der Annahme, dass keine Daten länger als 4 Ziffern sind, warum die vollständigen 12 Ziffern des Feldes entpacken?), Aber ansonsten ist das Parsing nur ein p.i.t.a.

Viel Glück mit Ihren flachen Daten. Fricking Legacy Junk, wie ich es hasse.

3

Sind Sie sicher, dass Sie für diese Aufgabe prozessorgebunden sind? Die Berechnung ist einfach genug, um zu vermuten, dass der gesamte Prozess wahrscheinlich E/A-gebunden ist. In diesem Fall wird die Optimierung für das schnellere Entpacken nicht viel Zeit gewinnen.

Wenn Sie tatsächlich Prozessor gebunden sind, scheint das beschriebene Problem ziemlich parallelisierbar zu sein, aber natürlich liegt der Teufel in den Details Ihrer Geschäftsberechnung.

+0

Ziemlich sicher, ja. Im realen Code sind E/A und Entpacken getrennt, anders als im obigen Beispiel. Parallelität ist ein guter Ruf - das machen wir schon. Würde aber immer noch weniger Zeit damit verbringen, Saiten zu schnitzen. –

+2

Für mich ist der größte Engpass die Datenbank selbst; Wenn es sich um flache Daten handelt, handelt es sich möglicherweise um eine schreckliche alte flache Datenbank. Ich komme um das Problem herum, indem ich die Daten in Intervallen zu einer modernen Datenbank herausziehe, aber für "Echtzeit" -Daten ist es fast immer die Datenbank, die die Verlangsamungen verursacht. – Satanicpuppy

+0

Ich muss zustimmen. Insgesamt verbringt unser Prozess die meiste Zeit damit, Daten in die Datenbank zu schlürfen.Wir können parallele Prozesse verwenden, um dies zu beschleunigen. Es fiel mir auf, dass so ein großer Teil der Perl-Zeit in so einem kleinen Teil des Codes verbracht wurde, dass hier Spielraum für eine einfache Optimierung besteht. –

1

Dies könnte auch etwas für XS sein - so dass Sie eine C-Funktion verwenden, um die Daten zu ändern. Ich könnte mir vorstellen, dass dies viel schneller ist als alles andere, da Sie manuell kontrollieren können, wann Daten wirklich kopiert werden.
Der Erstellungsprozess wird schwieriger, da Sie vom C-Compiler abhängig sind und zusätzliche Integrationsschritte benötigen.

+0

Danke. Wahrscheinlich würde der Gewinn für mein Projekt die zusätzliche Komplexität nicht rechtfertigen. Wie von Satanicpuppy bemerkt, gibt es andere Teile des Prozesses, die teurer sind. Könnte für jemand anderen mit einem ähnlichen Problem arbeiten. –

+3

Wenn der Großteil der CPU-Zeit in der Regex-Engine und einer eingebauten Funktion verbracht wird, dann wird die meiste Zeit in C land verbracht (d. H. Nicht in laufenden Ostrees und Handhabung von Perl-Datenstrukturen). Und es braucht einen guten Programmierer, um die Leute zu schlagen, die die Perl-VM geschrieben haben. Auspacken ist schnell! – tsee

1

Einfach machen es parallel gehen. Es ist trivial, und auf jeder noch so fernmodernen Maschine wird es schneller.

0

Der Benchmark einer substr-basierten Version unseres Codes legte nahe, dass er um 50% schneller sein könnte als unser vorhandenes Unpack. Vergleicht man die Codes in der tatsächlichen Anwendung, gab uns die Substr-Version eine 16% ige Reduzierung der Laufzeit. Dies ist nahe dem, was wir uns erhofft hatten, basierend auf der Benchmark und dem Profiling, auf das in der Frage Bezug genommen wird.

Diese Optimierung könnte für uns nützlich sein. Aber wir haben eine Migration auf ein neues Betriebssystem am Horizont, also werden wir abwarten und sehen, wie die Codes dort funktionieren, bevor wir fortfahren. Wir haben einen Test hinzugefügt, um die vergleichenden Benchmarks im Auge zu behalten.

Das Idiom wir jetzt haben, ist:

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

Gefolgt von selektiven Entfernung führenden Platz wie zuvor.

Danke für alle bisherigen Antworten. Ich werde Sinans akzeptieren, weil es für uns funktioniert hat, obwohl es scheinbar "einfach falsch" ist.

Verwandte Themen