it-swarm.com.de

Textverarbeitung - Python vs. Perl-Leistung

Hier ist mein Perl- und Python -Skript zur einfachen Textverarbeitung aus ca. 21 Protokolldateien, die jeweils ca. 300 KB bis maximal 1 MB x 5-mal wiederholt werden (insgesamt 125 Dateien, aufgrund des log 5 mal wiederholt).

Python-Code (Code geändert, um kompiliertes re und re.I Zu verwenden)

#!/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 erzeugt beide Codes genau die gleiche Ergebnisdatei mit 10.790 Zeilen. Und hier ist das Timing für Cygwins Perl- und Python-Implementierungen.

[email protected] /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

[email protected] /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 10,2 Sekunden mit Python und nur 1,9 Sekunden mit Perl 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 OR Es ist offensichtlich, dass Perl die Geschwindigkeit 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 bei jeder anderen Art der Erstellung des Quellcodes gewinnt Perl immer mit großem Abstand. Und nicht ein einziges Mal war Python besser für einfaches m/regex/ Abgleichen und Drucken.

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

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

Bitte schlagen Sie 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.

Für diese Frage kann man also mit Sicherheit den Schluss ziehen, dass Perl für einfache reguläre Ausdrücke für Hunderte oder Tausende von Textdateien in jeder Zeile übereinstimmt und die Ergebnisse in eine Datei schreibt (oder auf den Bildschirm druckt). Für diesen Job immer die beste Leistung bringen. 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ückgreifen (obskur für einen normalen Benutzer wie mich) und auch C nicht aufrufen, C++, Assembly-Bibliotheken von Python oder Perl. Wir haben keine Zeit, all diese zusätzlichen Schritte und Installationen für einen einfachen Textabgleich zu lernen.

Also, Perl rockt für Textverarbeitung und Regex.

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

Update 29.05.2013: Ein exzellenter Artikel, der einen ähnlichen Vergleich macht ist hier . Perl gewinnt erneut für den einfachen Textabgleich ... Weitere Informationen finden Sie im Artikel.

66
ihightower

Genau für diese Art von Aufgaben wurde Perl entwickelt, daher wundert es mich nicht, dass sie schneller sind.

Eine einfache Optimierung in Ihrem Python Code wäre das Vorkompilieren dieser regulären Ausdrücke, 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.

18
Josh Wright

Hypothese: Perl verbringt weniger Zeit mit dem Backtracking in Zeilen, die aufgrund von Optimierungen, die Python nicht vorhanden sind, 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
14
ikegami

Funktionsaufrufe sind in Python etwas 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 sehen. Wahrscheinlich nicht genug, um Perl zu schlagen.

4
Don O'Donnell

Ich erwarte, dass Perl schneller ist. Können Sie, wenn Sie nur neugierig sind, 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()

pdate 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 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 eine Menge Dinge, die irgendwie in einer allgemeineren Sprache erledigt werden müssen. Andererseits ist es recht einfach, den Python) - Code lesbarer zu machen, wenn Sie den nicht lesbaren Teil herausschieben:

#!/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 könnte die def input_files() an einer anderen Stelle platziert werden (z. B. in einem anderen Modul), oder sie kann wiederverwendet werden. Es ist möglich, sogar die while (<>) {...} von Perl leicht nachzuahmen, auch wenn dies syntaktisch nicht der gleiche Weg ist:

#!/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 das letzte for (im Prinzip) so einfach aussehen wie das while (<>) {...} von Perl. Solche Verbesserungen der Lesbarkeit sind in Perl schwieriger.

Wie auch immer, es wird das Python schneller machen. Perl wird hier wieder schneller. Perl ist eine Datei/text cruncher. Aber meiner Meinung nach ist Python eine bessere Programmiersprache für allgemeinere Zwecke.

1
pepr

Im Allgemeinen sind alle künstlichen Benchmarks böse. Wenn jedoch alles andere gleich ist (algorithmischer Ansatz), können Sie Verbesserungen auf relativer Basis vornehmen. Es sollte jedoch beachtet werden, dass ich Perl nicht verwende, also kann ich nicht für es argumentieren. Allerdings können Sie mit Python) versuchen, die Leistung mit Pyrex oder Cython zu verbessern, oder wenn Sie abenteuerlustig sind, können Sie es versuchen Konvertieren des Python - Codes in C++ über ShedSkin (funktioniert für die meisten Kernsprachen und einige - aber nicht alle - Kernmodule).

Dennoch können Sie einige der hier geposteten Tipps befolgen:

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

1
jrd1