【问题标题】:Text processing - Python vs Perl performance [closed]文本处理 - Python vs Perl 性能 [关闭]
【发布时间】:2012-09-29 09:39:34
【问题描述】:

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

Python 代码(代码修改为使用编译后的re 并使用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 秒即可完成这个简单的文本处理。

(更新)但是,在编译 re 版本的 Python 之后,现在在 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...不诉诸一些晦涩难懂的模块(对于像我这样的普通用户来说晦涩难懂),也没有调用 C、C++、程序集来自 Python 或 Perl 的库。我们没有时间为简单的文本匹配作业学习所有这些额外的步骤和安装。

因此,Perl 非常适合文本处理和正则表达式。

Python 在其他地方也有它的优势。

2013 年 5 月 29 日更新: 一篇进行类似比较的优秀文章 is here。 Perl 再次在简单文本匹配方面获胜...有关更多详细信息,请阅读文章。

【问题讨论】:

  • 模式是否只在 Python 中编译一次(就像在 Perl 中一样)?
  • 我想知道差异是否在于在不匹配的行中回溯所花费的时间。
  • 我会通过profiler 运行 Python 代码,以发现它在哪里花费时间。您也可以尝试使用 PCRE (Perl Compatible Regular Expressions) 而不是 Python 内置的正则表达式(这里是 another implementation),看看效果是否更好。
  • “因过于本地化而关闭”对我来说似乎太有趣和主观了。
  • 我之前看过基准测试,这表明 Perl 的正则表达式实现比 Python 快得多。否则它们的速度应该相当。

标签: python regex performance perl text-processing


【解决方案1】:

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

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

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

【讨论】:

  • 我既不是专家 perl 也不是 python 程序员。从普通的初学者到中级书籍,我以这样的方式使用 perl 和 python。如果我想获得真正的表现,我当然会使用你的建议,甚至使用汇编(如果我学过的话)。使用 perl 或 python 及其模块中现成的东西应该是我期望改进代码性能的唯一建议。我不希望使用其他一些神奇的流行语并花时间学习其余部分。请建议在正常 python 安装中存在的纯解决方案。
  • 我明白所有的人为基准都可能是邪恶的。但是,文本处理是一个简单的处理,这就是我通常每天都在做的事情。因此,如果 python 无法提高在原始 python 安装中使用某些基本语法的速度......(就像我对 perl 所做的那样)......我将不得不求助于 perl 来完成我的文本处理任务......并处理我必须处理的 100 或 100000 个文件......而且人们不得不承认,对于我的代码中给出的简单文本处理,python 速度很慢。但是,男孩,我希望使用 python 的简洁语法,但速度滞后.. 不要这么认为。
  • Python 中的正则表达式是通过模块提供的。 Perl 中的正则表达式具有内置语法,可以编译为内联(没有函数调用开销)。文本处理不必那么简单。无论如何,为每项任务使用更好的工具。我个人的经验是,稍微复杂一点的 Perl 程序在未来更难阅读和维护。
  • -1。这有什么“恶”?这是一个简单的练习,说明了两种语言之间的显着性能差异。如果不进行这样的测试,您应该如何比较两种工具的性能?用两种语言编写你的整个程序,这样它就不是“人造的”?当然,基准测试存在缺陷,但您已将其概括为一个非常愚蠢的规则。
【解决方案2】:

这正是 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 脚本保持一致,但是在没有先编译的情况下在循环中重复调用 re 在 Python 中是不好的做法。

【讨论】:

  • re 缓存最近使用的正则表达式,所以这可能不是一个大问题。
  • @nneonneo 我已经听过很多次了,并且我已经看到了re 源代码中执行缓存的行。但不知何故,我从未见过将两者置于同一数量级的基准测试,但有几个基准测试(包括我一秒钟前做的一个快速而肮脏的基准测试)将预编译选项的速度提高了好几倍。跨度>
  • 有趣。好吧,预编译正则表达式绝对是个好习惯,但我并没有真正注意性能差距。愿意分享数字吗?
【解决方案3】:

函数调用在 Python 中的时间有点贵。然而你有一个循环不变的函数调用来获取循环内的文件名:

fn = fileinput.filename()

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

【讨论】:

  • +1 好眼光,但是...好吧,但是文件名发生了变化。它不是循环不变量。无论如何,不​​使用fileinput 模块并通过文件名添加另一个外循环可能会更快。那么文件名将是不变的。
  • 一个有趣的点,但与两个正则表达式的处理时间相比,这必须是微不足道的。
【解决方案4】:

假设:Perl 在不匹配的行中花费更少的时间回溯,因为它具有 Python 没有的优化。

替换会得到什么

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

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

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

【讨论】:

    【解决方案5】:

    我希望 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() 可以放在其他地方(比如在另一个模块中),或者可以重复使用。甚至可以轻松地模仿 Perl 的 while (&lt;&gt;) {...},尽管语法上的方式不同:

    #!/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 (&lt;&gt;) {...} 一样简单(原则上)。这种可读性增强在 Perl 中更加困难。

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

    【讨论】:

    • @ihightower 请将您尝试的编辑作为新答案发布。
    • @pepr 我已将我的结果作为单独的答案发布。现在代码运行时间为 6.1 秒(比之前提高了 2 秒),而 perl 的运行时间为 1.8 秒。请阅读我的答案以获取更多信息。
    • @ihightower:使用with 结构会缩短一行。确实,嵌套的for 看起来很糟糕。但是,他们说具体做了什么:1)获取命令行参数,2)将每个参数扩展为一个全局掩码,3)如果它是一个文件名,打开它并处理它的行。
    • 既然文本处理是如此的通用,那么为什么 Python 不只是制作一个通用的内置标准模块,它可以应用于几乎所有情况......然后它可以提高它的性能对于像绝大多数人这样的普通用户......例如导入 TextTool 什么的,然后有一些标准的东西可以提高文本处理的性能。
    猜你喜欢
    • 2013-08-13
    • 1970-01-01
    • 2014-10-06
    • 2023-03-09
    • 2016-08-02
    • 2012-03-07
    • 2013-12-10
    • 1970-01-01
    • 2012-03-15
    相关资源
    最近更新 更多