如果RAM不是问题,逐行读取是否比将所有内容读取到RAM中然后访问更快? - Python

14 投票
4 回答
909 浏览
提问于 2025-04-17 16:51

如果服务器的内存(RAM)不是问题(我有将近200GB的内存),那么逐行读取数据和把所有数据一次性读入内存后再访问,哪个更快呢?每一行大约有200到500个Unicode字符。每个文件大约有200万行。

逐行读取

import codecs
for i in codecs.open('unicodefile','r','utf8'):
  print i

读取到内存

import codecs
for i in codecs.open('unicodefile','r','utf8').readlines():
  print i

4 个回答

6

看了提问者发的示例代码,我觉得对Python的理解有点误区。

比如:

“逐行读取”

import codecs
for i in codecs.open('unicodefile','r','utf8'):
  print i

上面的代码看起来是逐行读取的。然而,Python其实是把文件的内容一次性读到内存中,然后再把每一行处理。所以实际上,上面的for循环是把所有内容都读进了内存。

“读取到内存”

import codecs
for i in codecs.open('unicodefile','r','utf8').readlines():
  print i

我认为上面的情况和“逐行读取”的例子几乎是一样的。也就是说,Python还是把所有内容都读进了内存。

如果你想测试逐行读取的性能,你需要使用“readline()”,而不是“readlines()”或者不明确的for循环,因为后者可能暗示使用了“readlines()”。这个在StackOverflow的其他地方也有提到。

还有一个需要考虑的方面是文件系统的缓存。如果你对同一个文件运行相同的代码,就有可能因为文件系统的缓存影响结果。你提到你有200GB的内存,这足够缓存文件的内容,从而影响运行结果。

为了确保测试结果的准确性,你需要做以下几步:

1) 从已知来源复制大文件到一个新文件名。(文件系统不能是COW文件系统。)

2) 刷新文件系统缓存。

3) 对这个文件运行第一次测试。

4) 删除这个文件。

5) 从源头重新复制文件到另一个新文件名。

6) 刷新文件系统缓存。

7) 对新文件运行第二次测试。

这样可以更准确地测试文件加载时间。

如果你想一次性把整个文件加载到内存中,使用filehandle.read(要读取的字节数)可能会提供更快的块读取方式。

无论如何,供你参考:

http://docs.python.org/2/tutorial/inputoutput.html

8

你可以在自己的电脑上试试这个。 我创建了一个包含100万行的文件,下面是我测试的结果,记录了时间:

time python something.py > /dev/null

逐行读取:

real    0m4.878s
user    0m4.860s
sys     0m0.008s

读入内存:

real    0m0.981s
user    0m0.828s
sys     0m0.148s

当我尝试用200万行,每行300个字符的数据时,出现了内存错误,但上面的结果表明,读入内存的速度会更快。

15

我在一个大约1MB的字典单词文件上使用了cProfile工具。我读取了同一个文件3次。第一次读取整个文件是为了确保它被存储在缓存中,这样可以让测试更公平。下面是简单的代码:

def first_read():
    codecs.open(file, 'r', 'utf8').readlines()

def line_by_line():
    for i in codecs.open(file, 'r', 'utf8'):
        pass

def at_once():
    for i in codecs.open(file, 'r', 'utf8').readlines():
        pass

first_read()
cProfile.run('line_by_line()')
cProfile.run('at_once()')

接下来是结果:

逐行读取:

         366959 function calls in 1.762 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    1.762    1.762 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 codecs.py:322(__init__)
        1    0.000    0.000    0.000    0.000 codecs.py:395(__init__)
    14093    0.087    0.000    0.131    0.000 codecs.py:424(read)
    57448    0.285    0.000    0.566    0.000 codecs.py:503(readline)
    57448    0.444    0.000    1.010    0.000 codecs.py:612(next)
        1    0.000    0.000    0.000    0.000 codecs.py:651(__init__)
    57448    0.381    0.000    1.390    0.000 codecs.py:681(next)
        1    0.000    0.000    0.000    0.000 codecs.py:686(__iter__)
        1    0.000    0.000    0.000    0.000 codecs.py:841(open)
        1    0.372    0.372    1.762    1.762 test.py:9(line_by_line)
    13316    0.011    0.000    0.023    0.000 utf_8.py:15(decode)
        1    0.000    0.000    0.000    0.000 {_codecs.lookup}
    27385    0.027    0.000    0.027    0.000 {_codecs.utf_8_decode}
    98895    0.011    0.000    0.011    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
    13316    0.099    0.000    0.122    0.000 {method 'endswith' of 'unicode' objects}
       27    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
    14069    0.027    0.000    0.027    0.000 {method 'read' of 'file' objects}
    13504    0.020    0.000    0.020    0.000 {method 'splitlines' of 'unicode' objects}
        1    0.000    0.000    0.000    0.000 {open}

一次性读取:

         15 function calls in 0.023 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.023    0.023 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 codecs.py:322(__init__)
        1    0.000    0.000    0.000    0.000 codecs.py:395(__init__)
        1    0.000    0.000    0.003    0.003 codecs.py:424(read)
        1    0.000    0.000    0.014    0.014 codecs.py:576(readlines)
        1    0.000    0.000    0.000    0.000 codecs.py:651(__init__)
        1    0.000    0.000    0.014    0.014 codecs.py:677(readlines)
        1    0.000    0.000    0.000    0.000 codecs.py:841(open)
        1    0.009    0.009    0.023    0.023 test.py:13(at_once)
        1    0.000    0.000    0.000    0.000 {_codecs.lookup}
        1    0.003    0.003    0.003    0.003 {_codecs.utf_8_decode}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.001    0.001    0.001    0.001 {method 'read' of 'file' objects}
        1    0.010    0.010    0.010    0.010 {method 'splitlines' of 'unicode' objects}
        1    0.000    0.000    0.000    0.000 {open}

从结果中可以看出,一次性读取整个文件要快得多,但如果文件太大,就有可能出现内存错误。

撰写回答