Ano*_*ing 9 python perl file-io
我在使用Python处理大文件时遇到问题.我正在做的就是
f = gzip.open(pathToLog, 'r')
for line in f:
counter = counter + 1
if (counter % 1000000 == 0):
print counter
f.close
Run Code Online (Sandbox Code Playgroud)
这需要大约10m25s才能打开文件,读取行并递增此计数器.
在perl中,处理相同的文件并执行更多(一些正则表达式),整个过程大约需要1m17秒.
Perl代码:
open(LOG, "/bin/zcat $logfile |") or die "Cannot read $logfile: $!\n";
while (<LOG>) {
if (m/.*\[svc-\w+\].*login result: Successful\.$/) {
$_ =~ s/some regex here/$1,$2,$3,$4/;
push @an_array, $_
}
}
close LOG;
Run Code Online (Sandbox Code Playgroud)
任何人都可以建议我能做些什么来使Python解决方案以与Perl解决方案类似的速度运行?
编辑 我尝试解压缩文件并使用open而不是gzip.open处理它,但这只会将总时间更改为大约4m14.972s,这仍然太慢.
我还删除了modulo和print语句,并用pass替换它们,所以现在所做的就是从一个文件移到另一个文件.
在Python中(至少<= 2.6.x),gzip格式解析在Python中实现(通过zlib).更多的是,它似乎做了一些奇怪的事情,即,将文件末尾解压缩到内存,然后丢弃超出请求的读取大小的所有内容(然后再次进行下一次读取).免责声明:我刚刚看了gzip.read()
3分钟,所以我在这里错了.无论我对gzip.read()的理解是否正确,gzip模块似乎都没有针对大数据量进行优化.尝试做与Perl相同的事情,即启动外部过程(例如参见模块subprocess
).
编辑 实际上,我错过了OP关于普通文件I/O和压缩一样慢的说法(感谢ire_and_curses指出它).这让我觉得不太可能,所以我做了一些测量......
from timeit import Timer
def w(n):
L = "*"*80+"\n"
with open("ttt", "w") as f:
for i in xrange(n) :
f.write(L)
def r():
with open("ttt", "r") as f:
for n,line in enumerate(f) :
if n % 1000000 == 0 :
print n
def g():
f = gzip.open("ttt.gz", "r")
for n,line in enumerate(f) :
if n % 1000000 == 0 :
print n
Run Code Online (Sandbox Code Playgroud)
现在,运行它......
>>> Timer("w(10000000)", "from __main__ import w").timeit(1)
14.153118133544922
>>> Timer("r()", "from __main__ import r").timeit(1)
1.6482770442962646
# here i switched to a terminal and made ttt.gz from ttt
>>> Timer("g()", "from __main__ import g").timeit(1)
Run Code Online (Sandbox Code Playgroud)
...在喝茶休息并发现它还在运行之后,我已经杀了它,抱歉.然后我尝试了100'000行而不是10'000'000:
>>> Timer("w(100000)", "from __main__ import w").timeit(1)
0.05810999870300293
>>> Timer("r()", "from __main__ import r").timeit(1)
0.09662318229675293
# here i switched to a terminal and made ttt.gz from ttt
>>> Timer("g()", "from __main__ import g").timeit(1)
11.939290046691895
Run Code Online (Sandbox Code Playgroud)
模块gzip的时间是O(file_size**2),因此数量为数百万的行数,gzip读取时间不能与普通读取时间相同(我们看到实验证实).Anonymouslemming,请再次检查.
如果你谷歌"为什么python gzip慢"你会发现很多关于这个的讨论,包括改进Python 2.7和3.2的补丁.与此同时,像在Perl中那样使用zcat,这是快速的邪恶.你的(第一个)函数需要大约4.19s和5MB压缩文件,第二个函数需要0.78s.但是,我不知道你的未压缩文件发生了什么.如果我解压缩日志文件(apache日志)并使用简单的Python open(文件)和Popen('cat')对它们运行两个函数,Python比cat(0.48s)更快(0.17s).
#!/usr/bin/python import gzip from subprocess import PIPE, Popen import sys import timeit #pathToLog = 'big.log.gz' # 50M compressed (*10 uncompressed) pathToLog = 'small.log.gz' # 5M "" def test_ori(): counter = 0 f = gzip.open(pathToLog, 'r') for line in f: counter = counter + 1 if (counter % 100000 == 0): # 1000000 print counter, line f.close def test_new(): counter = 0 content = Popen(["zcat", pathToLog], stdout=PIPE).communicate()[0].split('\n') for line in content: counter = counter + 1 if (counter % 100000 == 0): # 1000000 print counter, line if '__main__' == __name__: to = timeit.Timer('test_ori()', 'from __main__ import test_ori') print "Original function time", to.timeit(1) tn = timeit.Timer('test_new()', 'from __main__ import test_new') print "New function time", tn.timeit(1)