Python文本文件处理速度问题

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替换它们,所以现在所做的就是从一个文件移到另一个文件.

atz*_*tzz 9

在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,请再次检查.

  • 这很有意思,但不是重点吗?无论这是否真实,OP指出在Python中循环未压缩文件(因此根本不使用`gzip`模块)也很慢. (3认同)

Rea*_*gle 5

如果你谷歌"为什么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)