Textverarbeitung - Python vs Perl Leistung [geschlossen]

74

Hier ist mein Perl- und Python-Skript für eine einfache Textverarbeitung aus ungefähr 21 Protokolldateien mit jeweils ungefähr 300 KB bis 1 MB (maximal) x 5-mal wiederholt (insgesamt 125 Dateien, da das Protokoll 5-mal wiederholt wurde).

Python-Code (Code geändert, um kompiliert reund verwendet zu werden re.I)

#!/usr/bin/python

import re
import fileinput

exists_re = re.compile(r'^(.*?) INFO.*Such a record already exists', re.I)
location_re = re.compile(r'^AwbLocation (.*?) insert into', re.I)

for line in fileinput.input():
    fn = fileinput.filename()
    currline = line.rstrip()

    mprev = exists_re.search(currline)

    if(mprev):
        xlogtime = mprev.group(1)

    mcurr = location_re.search(currline)

    if(mcurr):
        print fn, xlogtime, mcurr.group(1)

Perl Code

#!/usr/bin/perl

while (<>) {
    chomp;

    if (m/^(.*?) INFO.*Such a record already exists/i) {
        $xlogtime = $1;
    }

    if (m/^AwbLocation (.*?) insert into/i) {
        print "$ARGV $xlogtime $1\n";
    }
}

Und auf meinem PC generiert jeder Code genau die gleiche Ergebnisdatei mit 10.790 Zeilen. Und hier ist das Timing für Cygwins Perl- und Python-Implementierungen.

User@UserHP /cygdrive/d/tmp/Clipboard
# time /tmp/scripts/python/afs/process_file.py *log* *log* *log* *log* *log* >
summarypy.log

real    0m8.185s
user    0m8.018s
sys     0m0.092s

User@UserHP /cygdrive/d/tmp/Clipboard
# time /tmp/scripts/python/afs/process_file.pl *log* *log* *log* *log* *log* >
summarypl.log

real    0m1.481s
user    0m1.294s
sys     0m0.124s

Ursprünglich dauerte es mit Python 10,2 Sekunden und mit Perl nur 1,9 Sekunden für diese einfache Textverarbeitung.

(UPDATE), aber nach der kompilierten reVersion von Python dauert es jetzt 8,2 Sekunden in Python und 1,5 Sekunden in Perl. Trotzdem ist Perl viel schneller.

Gibt es eine Möglichkeit, die Geschwindigkeit von Python überhaupt zu verbessern, ODER es ist offensichtlich, dass Perl die schnelle für die einfache Textverarbeitung sein wird.

Übrigens war dies nicht der einzige Test, den ich für die einfache Textverarbeitung durchgeführt habe ... Und auf jede andere Art und Weise, wie ich den Quellcode erstelle, gewinnt Perl immer mit großem Vorsprung. Und nicht ein einziges Mal schnitt Python besser ab, wenn es um einfache m/regex/Übereinstimmungen und Drucke ging.

Bitte schlagen Sie nicht vor, C, C ++, Assembly, andere Python-Varianten usw. zu verwenden.

Ich suche nach einer Lösung mit Standard Python mit seinen eingebauten Modulen im Vergleich zu Standard Perl (nicht einmal mit den Modulen). Junge, ich möchte Python aufgrund seiner Lesbarkeit für alle meine Aufgaben verwenden, aber um die Geschwindigkeit aufzugeben, glaube ich nicht.

Schlagen Sie daher vor, wie der Code verbessert werden kann, um vergleichbare Ergebnisse mit Perl zu erzielen.

UPDATE: 18.10.2012

Wie andere Benutzer vorgeschlagen haben, hat Perl seinen Platz und Python seinen.

Bei dieser Frage kann man also mit Sicherheit den Schluss ziehen, dass Perl für eine einfache Regex-Übereinstimmung in jeder Zeile für Hunderte oder Tausende von Textdateien und das Schreiben der Ergebnisse in eine Datei (oder das Drucken auf dem Bildschirm) immer, immer die Leistung für diesen Job GEWINNT . So einfach ist das.

Bitte beachten Sie, dass, wenn ich sage, dass Perl an Leistung gewinnt ... nur Standard-Perl und Python verglichen werden ... nicht auf einige obskure Module zurückgegriffen wird (obskur für einen normalen Benutzer wie mich) und auch keine C-, C ++ - Assembly-Bibliotheken aus Python aufgerufen werden oder Perl. Wir haben keine Zeit, all diese zusätzlichen Schritte und die Installation für einen einfachen Textabgleich zu lernen.

Perl rockt also für Textverarbeitung und Regex.

Python hat seinen Platz, um an anderen Orten zu rocken.

Update 29.05.2013: Ein ausgezeichneter Artikel, der ähnliche Vergleiche anstellt , ist hier . Perl gewinnt erneut für den einfachen Textabgleich ... Und für weitere Details lesen Sie den Artikel.

ihightower
quelle
Werden die Muster in Python nur einmal kompiliert (wie in Perl)?
Ikegami
1
Ich frage mich, ob der Unterschied in der Zeit liegt, die für das Zurückverfolgen in Zeilen aufgewendet wird, die nicht übereinstimmen.
Ikegami
3
Ich würde den Python-Code durch einen Profiler ausführen, um herauszufinden , wo er seine Zeit verbringt. Sie können auch versuchen, PCRE (Perl Compatible Regular Expressions) anstelle des in Regexes integrierten Python zu verwenden (hier ist eine andere Implementierung ) und zu prüfen, ob dies besser funktioniert.
Schwern
3
"Geschlossen als zu lokalisiert" erscheint mir zu lustig und subjektiv.
Pepr
1
Ich habe schon Benchmarsk gesehen, was darauf hindeutet, dass Perls Regexp-Implementierung viel schneller ist als Pythons. Ansonsten sollten sie eine vergleichbare Geschwindigkeit haben.
Leon Timmermans

Antworten:

18

Dies ist genau das, wofür Perl entwickelt wurde. Es überrascht mich also nicht, dass es schneller ist.

Eine einfache Optimierung in Ihrem Python-Code besteht darin, diese regulären Ausdrücke vorkompilieren, damit sie nicht jedes Mal neu kompiliert werden.

exists_re = re.compile(r'^(.*?) INFO.*Such a record already exists')
location_re = re.compile(r'^AwbLocation (.*?) insert into')

Und dann in Ihrer Schleife:

mprev = exists_re.search(currline)

und

mcurr = location_re.search(currline)

Das allein bringt Ihr Python-Skript nicht auf magische Weise in Einklang mit Ihrem Perl-Skript, aber das wiederholte Aufrufen von re in einer Schleife ohne vorheriges Kompilieren ist in Python eine schlechte Praxis.

Josh Wright
quelle
3
reZwischenspeichert kürzlich verwendete reguläre Ausdrücke, daher ist dies wahrscheinlich kein großes Problem.
Nneonneo
5
@nneonneo Ich habe das schon oft gehört und ich habe die Zeilen im reQuellcode gesehen, die das Caching durchführen. Aber irgendwie habe ich noch nie einen Benchmark gesehen, der die beiden in die gleiche Größenordnung bringt, sondern mehrere Benchmarks (einschließlich eines schnellen und schmutzigen, den ich vor einer Sekunde durchgeführt habe), die die Vorkompilierungsoption um ein Vielfaches beschleunigen.
3
Interessant. Nun, es ist definitiv eine gute Praxis, Regexe vorkompilieren, aber ich habe die Leistungslücke nicht wirklich beachtet. Möchtest du die Zahlen teilen?
Nneonneo
14

Hypothese: Perl verbringt weniger Zeit mit dem Zurückverfolgen von Zeilen, die aufgrund von Optimierungen, die Python nicht hat, nicht übereinstimmen.

Was bekommen Sie durch Ersetzen

^(.*?) INFO.*Such a record already exists

mit

^((?:(?! INFO).)*?) INFO.*Such a record already 

oder

^(?>(.*?) INFO).*Such a record already exists
Ikegami
quelle
4

Funktionsaufrufe sind in Python zeitaufwändig. Und dennoch haben Sie einen schleifeninvarianten Funktionsaufruf, um den Dateinamen in der Schleife abzurufen:

fn = fileinput.filename()

Bewegen Sie diese Zeile über die forSchleife, und Sie sollten eine Verbesserung Ihres Python-Timings feststellen. Wahrscheinlich nicht genug, um Perl zu schlagen.

Don O'Donnell
quelle
1
+1 für das gute Auge, aber ... Nun, aber der Dateiname ändert sich. Es ist keine Schleifeninvariante. Auf jeden Fall kann es schneller sein, das fileinputModul nicht zu verwenden und eine weitere äußere Schleife durch die Dateinamen hinzuzufügen. Dann wäre der Dateiname die Invariante.
Pepr
1
Ein interessanter Punkt, der jedoch im Vergleich zur Verarbeitungszeit von zwei regulären Ausdrücken winzig sein muss.
dan1111
1

Im Allgemeinen sind alle künstlichen Benchmarks böse. Wenn jedoch alles andere gleich ist (algorithmischer Ansatz), können Sie relative Verbesserungen vornehmen. Es sollte jedoch beachtet werden, dass ich Perl nicht verwende, daher kann ich nicht für ihn argumentieren. Mit Python können Sie jedoch versuchen, Pyrex oder Cython zu verwenden, um die Leistung zu verbessern. Wenn Sie abenteuerlustig sind, können Sie versuchen, den Python-Code über ShedSkin (das für die meisten Kernsprachen und einige - aber nicht alle Kernmodule) funktioniert, in C ++ zu konvertieren .

Trotzdem können Sie einige der hier veröffentlichten Tipps befolgen:

http://wiki.python.org/moin/PythonSpeed/PerformanceTips

jrd1
quelle
Ich bin weder ein Perl-Experte noch ein Python-Programmierer. Ich benutze Perl und Python so, wie ich es von einem gewöhnlichen Anfänger bis zu einem Buch für Fortgeschrittene gelesen habe. Wenn ich die wirkliche Leistung haben möchte, werde ich sicherlich Ihre Vorschläge verwenden und sogar die Montage verwenden (falls ich es jemals lernen sollte). Die Verwendung von Perl oder Python und seinen Modulen sollte der einzige Vorschlag sein, den ich zur Verbesserung des Leistungscodes erwarte. Ich erwarte nicht, andere magische Schlagworte zu verwenden und die Zeit zu verbringen, um den Rest zu lernen. Bitte schlagen Sie eine reine Lösung vor, die in der nromal Python-Installation vorhanden ist.
ihightower
1
Ich verstehe, dass alle künstlichen Benchmarks böse sein könnten. Aber die Textverarbeitung ist einfach und das mache ich normalerweise Tag für Tag. Wenn Python die Geschwindigkeit bei der Verwendung einer grundlegenden Syntax in der ursprünglichen Python-Installation nicht verbessern kann ... (genau wie bei Perl) ... muss ich für meine Textverarbeitungsaufgaben auf Perl zurückgreifen und verarbeiten Die 100er oder 100000er Dateien, die ich verarbeiten muss ... und man muss zugeben, dass Python für die einfache Textverarbeitung, wie in meinem Code angegeben, langsam ist. Aber, Junge, möchte ich Python für seine saubere Syntax verwenden, aber mit Verzögerung der Geschwindigkeit. Glaube nicht.
ihightower
Regelmäßige Ausdrücke in Python werden über das Modul bereitgestellt. Reguläre Ausdrücke in Perl haben die integrierte Syntax und können als Inlines kompiliert werden (keine Kosten für Funktionsaufrufe). Die Textverarbeitung muss nicht so einfach sein. Verwenden Sie auf jeden Fall für jede Aufgabe ein besseres Werkzeug. Meine persönliche Erfahrung ist, dass etwas komplexere Perl-Programme in Zukunft viel schwieriger zu lesen und zu warten sind.
Pepr
9
-1. Was ist daran "böse"? Es ist eine einfache Übung, die einen signifikanten Leistungsunterschied zwischen den beiden Sprachen zeigt. Wie genau sollen Sie die Leistung zweier Tools vergleichen, wenn nicht mit einem solchen Test? Schreiben Sie Ihr gesamtes Programm in beiden Sprachen, damit es nicht "künstlich" ist? Sicher, es gibt Fallstricke beim Benchmarking, aber Sie haben das auf eine sehr dumme Regel verallgemeinert.
dan1111
1

Ich erwarte, dass Perl schneller ist. Wenn Sie nur neugierig sind, können Sie Folgendes versuchen?

#!/usr/bin/python

import re
import glob
import sys
import os

exists_re = re.compile(r'^(.*?) INFO.*Such a record already exists', re.I)
location_re = re.compile(r'^AwbLocation (.*?) insert into', re.I)

for mask in sys.argv[1:]:
    for fname in glob.glob(mask):
        if os.path.isfile(fname):
            f = open(fname)
            for line in f:
                mex = exists_re.search(line)
                if mex:
                    xlogtime = mex.group(1)

                mloc = location_re.search(line)
                if mloc:
                    print fname, xlogtime, mloc.group(1)
            f.close()

Update als Reaktion auf "es ist zu komplex" .

Natürlich sieht es komplexer aus als die Perl-Version. Das Perl wurde um die regulären Ausdrücke herum aufgebaut. Auf diese Weise können Sie kaum eine interpretierte Sprache finden, die in regulären Ausdrücken schneller ist. Die Perl-Syntax ...

while (<>) {
    ...
}

... verbirgt auch viele Dinge, die irgendwie in einer allgemeineren Sprache erledigt werden müssen. Andererseits ist es ziemlich einfach, den Python-Code besser lesbar zu machen, wenn Sie den unlesbaren Teil herausnehmen:

#!/usr/bin/python

import re
import glob
import sys
import os

def input_files():
    '''The generator loops through the files defined by masks from cmd.'''
    for mask in sys.argv[1:]:
        for fname in glob.glob(mask):
            if os.path.isfile(fname):
                yield fname


exists_re = re.compile(r'^(.*?) INFO.*Such a record already exists', re.I)
location_re = re.compile(r'^AwbLocation (.*?) insert into', re.I)

for fname in input_files():
    with open(fname) as f:        # Now the f.close() is done automatically
        for line in f:
            mex = exists_re.search(line)
            if mex:
                xlogtime = mex.group(1)

            mloc = location_re.search(line)
            if mloc:
                print fname, xlogtime, mloc.group(1)

Hier def input_files()könnte das an anderer Stelle platziert werden (z. B. in einem anderen Modul) oder es kann wiederverwendet werden. Es ist möglich, sogar die Perls while (<>) {...}leicht nachzuahmen , auch wenn dies syntaktisch nicht auf die gleiche Weise geschieht:

#!/usr/bin/python

import re
import glob
import sys
import os

def input_lines():
    '''The generator loops through the lines of the files defined by masks from cmd.'''
    for mask in sys.argv[1:]:
        for fname in glob.glob(mask):
            if os.path.isfile(fname):
                with open(fname) as f: # now the f.close() is done automatically
                    for line in f:
                        yield fname, line

exists_re = re.compile(r'^(.*?) INFO.*Such a record already exists', re.I)
location_re = re.compile(r'^AwbLocation (.*?) insert into', re.I)

for fname, line in input_lines():
    mex = exists_re.search(line)
    if mex:
        xlogtime = mex.group(1)

    mloc = location_re.search(line)
    if mloc:
        print fname, xlogtime, mloc.group(1)

Dann kann der letzte for(im Prinzip) so einfach aussehen wie der Perl while (<>) {...}. Solche Verbesserungen der Lesbarkeit sind in Perl schwieriger.

Auf jeden Fall wird das Python-Programm dadurch nicht schneller. Perl wird hier wieder schneller sein. Perl ist ein Datei- / Text-Cruncher. Aber meiner Meinung nach ist Python eine bessere Programmiersprache für allgemeinere Zwecke.

pepr
quelle
@ihightower Bitte posten Sie stattdessen Ihre versuchte Bearbeitung als neue Antwort.
Craig Ringer
@pepr Ich habe meine Ergebnisse als separate Antwort veröffentlicht. Jetzt läuft der Code in 6,1 Sekunden (2 Sekunden Verbesserung gegenüber früher) im Vergleich zu 1,8 Sekunden von Perl. Bitte lesen Sie meine Antwort für weitere Informationen.
ihightower
@ihightower: Mit dem withKonstrukt wäre es eine Zeile kürzer. Es ist wahr, dass das verschachtelte forschrecklich aussieht. Sie sagen jedoch, was genau getan wird: 1) Abrufen der Befehlszeilenargumente, 2) Erweitern jedes Arguments als Glob-Maske, 3) Wenn es sich um einen Dateinamen handelt, öffnen Sie es und verarbeiten Sie seine Zeilen.
Pepr
Da die Textverarbeitung so universell ist, stellt Python nicht einfach ein integriertes Standardmodul her, das so allgemein gehalten ist, dass es auf fast alle Fälle angewendet werden kann. Es kann dann die Leistung für normale Benutzer verbessern, wie die überwiegende Mehrheit von Leute ... zum Beispiel TextTool importieren oder so, dann haben Sie einige Standard-Sachen, die die Leistung der Textverarbeitung verbessern.
ihightower