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 re
und 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 re
Version 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.
quelle
Antworten:
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.
quelle
re
Zwischenspeichert kürzlich verwendete reguläre Ausdrücke, daher ist dies wahrscheinlich kein großes Problem.re
Quellcode 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.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
quelle
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
for
Schleife, und Sie sollten eine Verbesserung Ihres Python-Timings feststellen. Wahrscheinlich nicht genug, um Perl zu schlagen.quelle
fileinput
Modul nicht zu verwenden und eine weitere äußere Schleife durch die Dateinamen hinzuzufügen. Dann wäre der Dateiname die Invariante.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
quelle
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 Perlswhile (<>) {...}
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 Perlwhile (<>) {...}
. 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.
quelle
with
Konstrukt wäre es eine Zeile kürzer. Es ist wahr, dass das verschachteltefor
schrecklich 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.