意外性能下降

2024-03-29 07:28:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我必须解析一个巨大的(250MB)文本文件,由于某些原因,它只是一行,导致我尝试的每个文本编辑器(Notepad++、VisualStudio、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倍。你知道吗

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

引擎盖下到底发生了什么事,我错过了?你知道吗


Tags: read字节bufferline原因逻辑find性能
2条回答

您的第二个版本不仅工作较慢,而且工作不正确。你知道吗

在第一个版本中,您用赋值(line = buffer[i+1:])重置line,而在第二个版本中,您只附加到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

如果我正确理解了您要做的事情,那么两个版本的代码都是错误的。就像@Leon在第二个版本中说的,你在ProcessLine(line)之后缺少了line = "",而在第一个版本中,只有第一行是正确的,如果行比buffer短,你只在line += buffer[:i]中使用了buffer的第一部分,但是问题出在这一行line = buffer[i+1:],所以如果你的line有1000个字符长,而且buffer是10000个字符长,那么当您使用line += buffer[:i]时,您的行将是9000个字符长,可能包含多行。从阅读:

这样做相当有效,但是,可能会发生这样的情况,即一行比缓冲区短,这会导致我跳过一行

我想你已经意识到了这一点,但我之所以要写得详细,是因为这也是为什么你的第一个版本工作得更快的原因。你知道吗

在解释了这一点之后,我认为最好的方法是读取孔文件,然后拆分文本以获得行,因此您的代码如下所示:

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

然后你就可以用这样的方法:

for line in l:
    ProcessLine(line)

要得到listl,我只用了不到2秒钟的时间。你知道吗

PS:用记事本打开大文件(比如250MB)应该不会有问题,我甚至打开了500MB的文件。你知道吗

相关问题 更多 >