Processamento de texto-Python vs desempenho Perl [fechado]

Aqui está o meu programa Perl e Python para fazer algum processamento de texto simples a partir de cerca de 21 ficheiros log, cada um com cerca de 300 KB a 1 MB (máximo) x 5 vezes repetido (total de 125 ficheiros, devido ao log repetido 5 vezes).

código Python (Código modificado para usar o compilado re e usando 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)

Código Perl

#!/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";
    }
}

e, no meu PC, ambos os códigos geram exactamente o mesmo ficheiro de resultados de 10.790 linhas. E, aqui está o timing feito no Cygwin's Implementações Perl e Python.

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

originalmente, levou 10,2 segundos usando Python e apenas 1,9 segundos usando Perl para este simples processamento de texto.

(UPDATE) but, after the compiled re version of Python, it now takes 8.2 seconds in Python and 1.5 seconds in Perl. Ainda Perl é muito mais rápido.

existe uma maneira de melhorar a velocidade do Python ou é óbvio que o Perl será o rápido para processamento de texto simples.

A propósito ... este não foi o único teste que fiz para processamento de texto simples... E, de cada forma diferente que faço o código fonte, sempre Perl ganha por uma grande margem. E, nem uma vez o Python teve um desempenho melhor para um jogo simples m/regex/ e material de impressão.

por favor não sugira a utilização de C, C++, Montagem, outros sabores de Python, etc.

Estou à procura de uma solução usando Python padrão com a sua integração módulos comparados com o Perl padrão (nem mesmo usando os módulos). Menino, Quero usar o Python para todas as minhas tarefas devido à sua legibilidade, mas ... para desistir do speed, acho que não.

Então, por favor, sugira como o código pode ser melhorado para ter comparáveis resultados com Perl.

actualizar: 2012-10-18

como outros utilizadores sugeriram, o Perl tem o seu lugar e o Python tem o seu.

Então, para esta pergunta, pode-se concluir com segurança que para uma correspondência regular simples em cada linha por centenas ou milhares de arquivos de texto e escrevendo os resultados para um arquivo (ou impressão para tela), Perl sempre, sempre ganhará no desempenho para este trabalho. Tão simples quanto isso.

Por favor, note que quando digo que o Perl ganha no desempenho... apenas o padrão Perl e Python são comparados... não recorrer a alguns módulos obscuros (obscuros para um utilizador normal como eu) e também não chamar bibliotecas de C, C++, assembly de Python ou Perl. Não temos tempo para aprender todos esses passos extras e instalação para um trabalho de correspondência de texto simples.

Então ... , Perl rocks for text processing and regex.

Python tem o seu lugar para rock em outros lugares.

Update 2013-05-29: um excelente artigo que faz comparações semelhantes está aqui . O Perl ganha novamente para uma correspondência de texto simples... E para mais detalhes, leia o artigo.

Author: Peter Mortensen, 2012-10-09

5 answers

Este é exactamente o tipo de coisa que o Perl foi projectado para fazer, por isso não me surpreende que seja mais rápido.

Uma otimização fácil no seu código Python seria pré-compilar esses regexes, então eles não estão sendo recompilados de cada vez.

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

E depois no teu círculo:

mprev = exists_re.search(currline)

E

mcurr = location_re.search(currline)

Que por si só não vai por magia o seu script Python em linha com o seu script Perl, mas chamar repetidamente a re em um loop sem compilar primeiro é mau pratica em Python.

 17
Author: Josh Wright, 2012-10-09 06:03:32

Hipótese: o Perl passa menos tempo a recuar em linhas que não correspondem devido às optimizações que tem que o Python não.

O que ganhas ao substituir

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

Com

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

Ou

^(?>(.*?) INFO).*Such a record already exists
 14
Author: ikegami, 2017-08-15 18:32:51

As chamadas de funções são um pouco caras em termos de tempo em Python. E ainda assim você tem uma chamada de função invariante de loop para obter o nome do arquivo dentro do loop:

fn = fileinput.filename()

Mova esta linha acima do laço for e deverá ver alguma melhoria no seu timing em Python. Provavelmente não o suficiente para derrotar o Perl.

 4
Author: Don O'Donnell, 2012-10-09 06:58:03
Em geral, todos os padrões artificiais são maus.No entanto, sendo tudo o resto igual (abordagem algorítmica), você pode fazer melhorias em uma base relativa. No entanto, deve-se notar que eu não uso Perl, então eu não posso argumentar a seu favor. Dito isto, com Python você pode tentar usar Pyrex ou Cython para melhorar o desempenho. Ou, se você é aventureiro, você pode tentar converter o código Python em C++ via ShedSkin (que funciona para a maioria dos linguagem Central, e alguns - mas não todos, dos módulos centrais).

No entanto, você pode seguir algumas das dicas postadas aqui:

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

 1
Author: jrd1, 2012-10-09 06:02:04
Espero que o Perl seja mais rápido. Só por curiosidade, podes tentar o seguinte?
#!/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()

Actualizar como reacção a "é demasiado complexo" .

Claro que parece mais complexo que a versão Perl. O Perl foi construído em torno das expressões regulares. Desta forma, você dificilmente pode encontrar linguagem interpretada que é mais rápido em expressões regulares. A sintaxe Perl...
while (<>) {
    ...
}

... também esconde um monte de coisas que têm de ser feitas de alguma forma em um mais geral idioma. Por outro lado, é muito fácil tornar o código Python mais legível Se você mover a parte ilegível para fora:

#!/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)

Aqui o def input_files() pode ser colocado noutro local (por exemplo, noutro módulo), ou pode ser reutilizado. É possível imitar até mesmo o Perl ' s while (<>) {...} facilmente, embora não da mesma forma sintaticamente:

#!/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)

Então o último for pode parecer tão fácil (em princípio) como o Perl while (<>) {...}. Estes melhoramentos de legibilidade são mais difíceis de Perl.

De qualquer forma, não vai tornar o programa Python mais rápido. O Perl será mais rápido aqui. O Perl é um cruncher de ficheiro/texto. Mas--na minha opinião -- Python é uma linguagem de programação melhor para propósitos mais gerais.
 1
Author: pepr, 2018-07-02 19:56:17