提高Python代码性能

时间:2015-06-01 16:45:56

标签: python perl

如何提高这段简单的python代码的性能? 不是re.search找到匹配线的最佳方法,因为它比Perl慢了近6倍,或者我做错了什么?

#!/usr/bin/env python

import re
import time
import sys

i=0
j=0
time1=time.time()
base_register =r'DramBaseAddress\d+'
for line in  open('rndcfg.cfg'):
    i+=1
    if(re.search(base_register, line)):
        j+=1
time2=time.time()

print (i,j)
print (time2-time1)    
print (sys.version)

此代码大约需要0.96秒才能完成(平均10次运行)
输出:

168197 2688
0.8597519397735596
3.3.2 (default, Sep 24 2013, 15:14:17)
[GCC 4.1.1]

而以下Perl代码在0.15秒内完成。

#!/usr/bin/env perl
use strict;
use warnings;

use Time::HiRes qw(time);

my $i=0;my $j=0;
my $time1=time;
open(my $fp, 'rndcfg.cfg');
while(<$fp>)
{
    $i++;
    if(/DramBaseAddress\d+/)
    {
        $j++;
    }
}
close($fp);
my $time2=time;

printf("%d,%d\n",$i,$j);
printf("%f\n",$time2-$time1);
printf("%s\n",$]);


输出:

168197,2688
0.135579
5.012001

编辑:更正了正则表达式 - 这使性能略有恶化

3 个答案:

答案 0 :(得分:5)

实际上,正则表达式的效率低于Python中的字符串方法。来自https://docs.python.org/2/howto/regex.html#use-string-methods

  

字符串有几种用于执行固定操作的方法   字符串,它们通常要快得多,因为实现是   一个针对此目的进行了优化的单个小C循环   大型的,更通用的正则表达式引擎。

re.search替换str.find将为您提供更好的运行时间。否则,使用其他人建议的in运算符也会被优化。

Python和&之间的速度差异Perl版本,我只是将其归结为每种语言的内在质量:text processing - python vs perl performance

答案 1 :(得分:1)

在这种情况下,您使用的是固定字符串,而不是正则表达式。

对于常规字符串,有更快的方法:

>>> timeit.timeit('re.search(regexp, "banana")', setup = "import re;     regexp=r'nan'")
1.2156920433044434
>>> timeit.timeit('"banana".index("nan")')
0.23752403259277344
>>> timeit.timeit('"banana".find("nan")')
0.2411658763885498

现在,这种文本处理是Perl(又名实用提取和报告语言)(又名病态折衷垃圾列表)的最佳点,并且多年来已经进行了广泛的优化。所有这些集体关注都会增加。

答案 2 :(得分:1)

尽管有缓存,但调用re.compile的开销很大。使用

is_wanted_line = re.compile(r"DramBaseAddress\d+").search

for i, line in enumerate(open('rndcfg.cfg')):
    if is_wanted_line(line):
        j += 1

代替。

此外,你可以做到

key = "DramBaseAddress"
is_wanted_line = re.compile(r"DramBaseAddress\d+").search

for i, line in enumerate(open('rndcfg.cfg')):
    if key in line and is_wanted_line(line):
        j += 1

进一步减少开销。

你也可以考虑做自己的缓冲:

key = b"DramBaseAddress"
is_wanted_line = re.compile(rb"DramBaseAddress\d+").search

with open("rndcfg.cfg", "rb") as file:
    rest = b""

    for chunk in iter(lambda: file.read(32768), b""):
        i += chunk.count(b"\n")
        chunk, _, rest = (rest + chunk).rpartition(b"\n")

        if key in rest and is_wanted_line(chunk):
            j += 1

    if key in rest and is_wanted_line(rest):
        j += 1

删除了行分割和编码开销。 (这并不完全相同,因为它没有考虑每个块的多个实例。这样的行为添加相对简单,但在您的情况下可能不是严格需要的。)

这有点重量级,但是如果你移除i += chunk.count(b"\n"),速度和Perl一样快 - 8倍!