文本处理-Python vs Perl性能[关闭]


74

这是我的Perl和Python脚本,可以对大约21个日志文件进行一些简单的文本处理,每个日志文件大约300 KB到1 MB(最大)x重复5次(总共125个文件,由于日志重复了5次)。

Python代码(修改为使用已编译re和using的代码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代码

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

而且,在我的PC上,两个代码均生成完全相同的10,790行结果文件。而且,这是Cygwin的Perl和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

最初,使用Python进行这种简单的文本处理仅需10.2秒,而使用Perl仅花费1.9秒。

(UPDATE),但是在rePython的编译版本之后,现在在Python中需要8.2秒,在Perl中需要1.5秒。Perl仍然更快。

有没有一种方法可以提高Python的速度,或者很明显,Perl将成为简单文本处理的快速工具。

顺便说一下,这不是我对简单文本处理所做的唯一测试...而且,我以各种不同的方式来编写源代码,始终总是Perl大获全胜。而且,在简单的m/regex/匹配和打印方面,Python从未有过更好的表现。

请不要建议使用C,C ++,Assembly,其他版本的Python等。

我正在寻找使用标准Python及其内置模块与标准Perl(甚至不使用模块)进行比较的解决方案。伙计,由于它的可读性,我希望使用Python来完成所有任务,但是为了放弃速度,我认为不是。

因此,请提出如何改进代码以使其与Perl具有可比结果的建议。

更新时间:2012-10-18

正如其他用户所建议的那样,Perl占有一席之地,Python占有一席之地。

因此,对于这个问题,可以放心地得出结论,对于每行成百上千个文本文件的简单正则表达式匹配并将结果写入文件(或打印到屏幕上),Perl总是会在这项工作中始终赢得性能。就这么简单。

请注意,当我说Perl赢得性能时……仅比较了标准Perl和Python……没有求助于一些晦涩难懂的模块(对于像我这样的普通用户而言晦涩难懂),也没有从Python调用C,C ++,汇编库或Perl。我们没有时间去学习所有这些额外的步骤和简单文本匹配作业的安装。

因此,Perl致力于文本处理和正则表达式。

Python在其他地方也占有一席之地。

2013年5月29日更新:此处有一篇出色的文章,做了类似的比较。Perl再次因简单的文本匹配而获胜。有关更多详细信息,请阅读文章。


这些模式是否仅在Python中(如在Perl中一样)编译一次?
ikegami 2012年

1
我想知道差异是否在于在不匹配的行中回溯所花费的时间。
ikegami 2012年

3
我将通过探查器运行Python代码以发现其花费时间。您也可以尝试使用PCRE(与Perl兼容的正则表达式),而不是使用内置于正则表达式中的Python(这是另一种实现),然后看看这样做是否更好。
Schwern 2012年

3
“封闭得太局限”对我来说太有趣和主观了。
pepr 2012年

1
在此之前,我已经看到了Benchmarsk的建议,即Perl的regexp实现比Python快得多。否则它们应该具有可比的速度。
里昂·蒂默曼斯

Answers:


18

这正是Perl设计要做的事情,因此它速度更快也不足为奇。

Python代码中的一种简单优化方法是预编译这些正则表达式,这样就不会每次都重新编译它们。

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

然后在您的循环中:

mprev = exists_re.search(currline)

mcurr = location_re.search(currline)

这本身不会神奇地使您的Python脚本与Perl脚本保持一致,但是在Python中反复调用re而又不先编译便是一个坏习惯。


3
re缓存最近使用的正则表达式,因此这可能不是一个大问题。
nneonneo 2012年

5
@nneonneo我已经听过很多次了,而且我已经看到了re源代码中进行缓存的行。但是以某种方式,我从未见过将这两个指标置于相同数量级的基准,但是有几个基准(包括我第二次进行的快速而肮脏的基准)使预编译选项的速度提高了几倍。

3
有趣。好的,预编译正则表达式绝对是个好习惯,但是我并没有真正注意性能差异。愿意分享数字吗?
nneonneo 2012年

14

假设:由于优化,Perl在Python不匹配的行中花更少的时间进行回溯。

更换后会得到什么

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

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

要么

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

4

就Python而言,函数调用在时间上有些昂贵。但是,您有一个循环不变的函数调用来获取循环内的文件名:

fn = fileinput.filename()

将此行for移到循环上方,您应该会看到Python时序有所改善。也许还不足以击败Perl。


1
+1是个不错的选择,但是...好吧,但是文件名却改变了。它不是循环不变式。无论如何,不​​使用该fileinput模块并通过文件名添加另一个外部循环可能会更快。那么文件名将是不变的。
pepr 2012年

1
有趣的一点是,与两个正则表达式的处理时间相比,它必须微不足道。
dan1111 '10

1

通常,所有人为基准都是邪恶的。但是,在其他所有条件都相同的情况下(算法方法),您可以在相对基础上进行改进。但是,应该注意的是,我不使用Perl,所以我不能赞成它。就是说,使用Python,您可以尝试使用PyrexCython来提高性能。或者,如果您喜欢冒险,可以尝试通过ShedSkin将Python代码转换为C ++ (适用于大多数核心语言,以及某些(但不是全部)核心模块)。

不过,您可以按照此处发布的一些提示进行操作:

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


我既不是专家perl也不是python程序员。从普通初学者到中级水平的书,我都使用了perl和python。如果我想获得真正的表现,当然我会使用您的建议,甚至使用汇编(如果我学过的话)。我希望使用perl或python及其模块中易于使用的功能,这是我希望改善代码性能的唯一建议。我不希望使用其他魔术流行语并花时间学习其余内容。请建议在nromal python安装中存在的纯解决方案。
ihightower 2012年

1
我知道所有人为基准都可能是邪恶的。但是,文本处理是一个简单的过程,这就是我通常每天都要做的事情。因此,如果python无法提高在原始python安装中使用某些基本语法的速度...(就像我对perl所做的那样)...我将不得不对我的文本处理任务求助于perl并进行处理我必须处理的文件数是100或100000 ...而人们将不得不承认python对于我的代码中给出的简单文本处理速度很慢。但是,男孩,我希望使用python的简洁语法,但是速度滞后..不要这样。
ihightower 2012年

通过模块提供Python中的常规表达式。Perl中的正则表达式具有内置语法,可以编译为内联(无函数调用开销)。文本处理不必那么简单。无论如何,为每个任务使用更好的工具。我的个人经验是,将来更难阅读和维护一些更复杂的Perl程序。
pepr 2012年

9
-1。什么是“邪恶”?这是一个简单的练习,说明了两种语言之间的显着性能差异。如果不通过这样的测试,您应该如何比较这两种工具的性能?用两种语言编写您的整个程序,以使其不是“人工的”吗?当然,基准测试存在一些陷阱,但是您已将其概括为一个非常愚蠢的规则。
dan1111 '10

1

我希望Perl更快。只是好奇,您可以尝试以下方法吗?

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

更新为对“它太复杂”的反应。

当然,它看起来比Perl版本更复杂。Perl是围绕正则表达式构建的。这样,您几乎找不到正则表达式中更快的解释语言。Perl语法...

while (<>) {
    ...
}

……还隐藏了许多必须以某种更通用的语言完成的事情。另一方面,如果将不可读部分移出,很容易使Python代码更具可读性:

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

在这里,def input_files()可以将其放置在其他位置(例如在另一个模块中),也可以重复使用它。甚至可以while (<>) {...}很容易地模仿Perl ,即使在语法上也不一样:

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

这样,最后一个for看上去就和Perl一样容易(原则上)while (<>) {...}。这种可读性增强在Perl中更加困难。

无论如何,它不会使Python程序更快。Perl在这里会更快。Perl文件/文本处理程序。但是-在我看来-Python是一种更通用的更好的编程语言。


@ihightower请改为将您尝试的编辑发布为新答案。
Craig Ringer

@pepr我将我的结果发布为单独的答案。与perl的1.8秒相比,现在的代码运行时间为6.1秒(比之前的改进2秒)。请阅读我的答案以获取更多信息。
ihightower 2012年

@ihightower:使用该with构造将缩短一行。嵌套for看起来确实很糟糕。但是,他们说到底要做什么:1)获取命令行参数,2)将每个参数扩展为全局掩码,3)如果它是文件名,请打开它并处理其行。
pepr 2012年

由于文本处理是如此普遍,因此为什么Python不仅会制作出如此通用的内置标准模块,以使其几乎可以应用于所有情况下,所以它可以提高普通用户(如绝大多数用户)的性能。人员...例如导入TextTool之类的东西,然后有一些标准的东西可以改善文本处理的性能。
ihightower
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.