2013-11-27 54 views
1

我对Python相当陌生。我正在构建一个脚本来浏览一个日志文件,就像我在Perl中做了上百次一样。我使用的是哈希计算某些字段出现在日志文件中,就像我已经做了一百时间在Perl,一拉:Python re.sub考虑慢?

for $line in (<FILE>) { 
    ($stuff1, $stuff2, $etc) = split(/\s+/, $line); 
    $stuff1 =~ s/something//; 
    $stuff2 =~ s/something//; 
    $count1{$stuff1}++; 
    $count2{$stuff2}++; 
} 
etc, print the hashes 

我的Python是这样的:

import re 
from collections import defaultdict 

cntdaemon = defaultdict(int) 
cntfaclevel = defaultdict(int) 
cnthost = defaultdict(int) 
redaemon1 = re.compile('\[[0-9]+\]') 
redaemon2= re.compile(':') 
refaclevel= re.compile(']') 
with open("/var/adm/messages", 'r') as infile: 
    for line in infile: 
     (m, d, t, host, daemon, junk, idno, faclevel, text) = line.split(' ',8) 
     daemon = re.sub(redaemon1, '', daemon) 
     daemon = re.sub(redaemon2, '', daemon) 
     cntdaemon[daemon] += 1 
     faclevel = re.sub(refaclevel, '', faclevel) 
     cntfaclevel[faclevel] += 1 
     cnthost[host] += 1 
print cntdaemon 
print cntfaclevel 
print cnthost 

我发现这个版本比Perl版本慢20倍左右。我已经运行了预编译正则表达式的测试用例,并对其进行了“即时编译”,并且可以忽略不计,所以我知道Python不会浪费时间编译正则表达式。我怀疑是每次我做一个“re.sub”的时候,它会花费我所有的时间来销毁和编译字符串。

所以,简单的问题 - 有一个成语做替代更快?

我想我总是可以尝试写一个函数来做到这一点,而不分配....是一种方法往往采取?人们可以通过它的字符串转换成一个列表,然后ITER,C/C++字符串风格(当然,我只是那个扔在那里...)

这可能是重要的(也就是为什么我不在示例中使用Counter()) - 我需要在Python 2.6.4中编写它。如果这在2.7或3中会快得多,就这么说吧。但我没有选择。

+3

1个字符的正则表达式可以通过'str.replace'或'str.translate(None,':')''加快速度。 – mgilson

+1

实际上,跳过不必要的're.sub'调用应该可以使脚本速度提高三倍,更不用说可读性的好处了。但个人而言,如果它必须快速,我会在Awk中做这种事情,而不是Python。 –

+0

有趣 - 我修剪系统日志外商投资企业后,我是从约4,000,000行读回至20000线和Py​​thon版本在大约相同的时间量Perl的版本上运行。所以: – wsanders

回答

1

首先,你应该尝试分析每一行的运行时间。制作一个这样的短程序并测量运行需要多长时间。

开始注释掉取决于他们的其他计算方法,没有线。

line = "blah blah..."  # typical line from the file 
for i in range(10000): # pretend there are 10000 lines 
    (m, d, t, host, daemon, junk, idno, faclevel, text) = line.split(' ',8) 
    daemon = re.sub(redaemon1, '', daemon) 
    daemon = re.sub(redaemon2, '', daemon) 
    cntdaemon[daemon] += 1 
    faclevel = re.sub(refaclevel, '', faclevel) 
    cntfaclevel[faclevel] += 1 
# cnthost[host] += 1 

现在你可以制定出时间cnthost[host] += 1多少需要

line = "blah blah..."  # typical line from the file 
for i in range(10000): # pretend there are 10000 lines 
    (m, d, t, host, daemon, junk, idno, faclevel, text) = line.split(' ',8) 
    daemon = re.sub(redaemon1, '', daemon) 
    daemon = re.sub(redaemon2, '', daemon) 
    cntdaemon[daemon] += 1 
    faclevel = re.sub(refaclevel, '', faclevel) 
# cntfaclevel[faclevel] += 1 
# cnthost[host] += 1  # takes 20 seconds 

现在你可以制定出cntfaclevel[faclevel] += 1多少时间

一旦你制定出热点 - 然后开始思考如何使其更快。

如果您可以添加一个典型的字符串,您正在处理的问题,我们将能够看到是否有特定的技巧可以使用,或者如果有什么特别的性能明智的表现。

+0

好吧,注释掉hash [key] = + 1行并没有太大的改变。逐一评论“重新”功能需要花费时间与注释的重新操作次数成比例。所以无论是re.sub()还是re.split(),re肯定是一个瓶颈。 我修整了从大约400万行读到20000行的系统日志,Python版本的运行时间与那些场景中的Perl版本大致相同。我会在下周再做一次检查。 str.replace和str。翻译建议是有帮助的,我只是想起了“in”关键字。 – wsanders

+0

@wsanders,我不能帮助你更好地优化它,而不会看到线条的样子。 –