如果RAM不是问题,逐行读取是否比将所有内容读取到RAM中然后访问更快? - Python
如果服务器的内存(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 个回答
看了提问者发的示例代码,我觉得对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(要读取的字节数)可能会提供更快的块读取方式。
无论如何,供你参考:
你可以在自己的电脑上试试这个。 我创建了一个包含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个字符的数据时,出现了内存错误,但上面的结果表明,读入内存的速度会更快。
我在一个大约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}
从结果中可以看出,一次性读取整个文件要快得多,但如果文件太大,就有可能出现内存错误。