意外性能下降

时间:2016-06-28 09:35:00

标签: python

我必须解析一个巨大的(250 MB)文本文件,由于某种原因只有一行,导致我尝试的每个文本编辑器(Notepad ++,Visual Studio,Matlab)都无法加载它。因此,我逐个阅读它,并在完全读取逻辑行(以#开头)时解析它:

f = open(filename, "rt")

line = ""
buffer = "blub"
while buffer != "":
    buffer = f.read(10000)
    i = buffer.find('#')
    if i != -1: # end of line found
        line += buffer[:i]
        ProcessLine(line)
        line = buffer[i+1:] # skip the '#'
    else: # still reading current line
        line += buffer

这种方法运行得相当不错,但是,它可能会发生,一条线比我的缓冲区短,这会导致我跳过一条线。所以我用

替换了循环
while buffer != "":
    buffer = f.read(10000)
    i = buffer.find('#')
    while i != -1:
        pixels += 1
        line += buffer[:i]
        buffer = buffer[i+1:]
        ProcessLine(line)
        i = buffer.find('#')
    line += buffer

,这就是诀窍。然而,这至少要慢一百倍,因此无法读取大文件。我没有真正看到,这是怎么发生的,我确实有一个内循环,但大多数时候它只重复一次。此外,我可能会复制缓冲区(buffer = buffer[i+1:]),我可以从中了解性能是否会下降一半,但我不知道这会让它变得慢100倍。

作为旁注:我的(逻辑)行大约是27.000字节。因此,如果我的缓冲区是10.000字节,我从不跳过第一个实现中的行,如果它是30.000,我这样做。然而,这似乎不会影响性能,即使第二次实现中的内部循环最多被评估一次,性能仍然很糟糕。

发生了什么事,我想念?

2 个答案:

答案 0 :(得分:2)

如果我理解你想要做什么,那么两个版本的代码都是错误的。就像@Leon在第二个版本中所说的那样,line = ""之后缺少ProcessLine(line),而在第一个版本中只是第一行是正确的,而且如果你想让行比缓冲更短,那么你只使用第一部分line += buffer[:i]中的缓冲区,但问题在于line = buffer[i+1:],所以如果line长度为1000个字符,buffer长度为10000个字符,那么当您使用{时{1}},您的行长度为9000个字符,可能包含多行。阅读:

“这样做效果相当不错,但可能会发生,一条线比我的缓冲区短,这会导致我跳过一条线”

我认为你意识到了这一点,但我正在详细撰写的原因是,这也是你的第一个版本更快运行的原因。

在解释之后,我认为最好的方法是读取孔文件然后拆分文本以获取行,所以你的代码看起来像这样:

line += buffer[:i]

并且你可以使用类似的东西:

f = open('textfile.txt', "rt")
buffer = f.read()
f.close()
l = buffer.split('#')

获取列表for line in l: ProcessLine(line) 我花了不到2秒钟。

PS:使用记事本打开大文件(如250MB)不会有问题,我甚至打开了500MB文件。

答案 1 :(得分:1)

您的第二个版本不仅工作速度慢,而且工作也不正确。

在您的第一个版本中,您使用分配(line)重置line = buffer[i+1:],而在第二个版本中,您只会附加到line。因此,在第二个版本中,最后line包含文件的全部内容,而不是#符号。

通过在处理后立即清除line来修复代码:

while buffer != "":
    buffer = f.read(10000)
    i = buffer.find('#')
    while i != -1:
        pixels += 1
        line += buffer[:i]
        buffer = buffer[i+1:]
        ProcessLine(line)
        line = ""               # sic!
        i = buffer.find('#')
    line += buffer