如果 RAM 不是问题,是逐行读取更快还是将所有内容读入 RAM 并访问它? - Python

Posted

技术标签:

【中文标题】如果 RAM 不是问题,是逐行读取更快还是将所有内容读入 RAM 并访问它? - Python【英文标题】:If RAM isn't a concern, is reading line by line faster or reading everything into RAM and access it? - Python 【发布时间】:2013-02-08 23:14:06 【问题描述】:

如果 RAM 不是问题(我在服务器上有接近 200GB 的空间),是逐行读取更快还是将所有内容读入 RAM 并访问它?每行将是一个大约 200-500 个 unicode 字符的字符串。每个文件有近 200 万行。

逐行

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

读入 RAM

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

【问题讨论】:

import timeit; timeit.timeit('''for i in codecs.open('unicodefile','r','utf8'): print i''', 'import codecs') 然后对第二种情况做同样的事情。 如果 RAM 不是问题(您知道可以将内容放入 RAM),则将所有内容放入 RAM。 RAM 的读取速度比旋转磁盘快一个数量级。内存层次结构是系统架构的基本原则。充分利用它们。 【参考方案1】:

没有什么能阻止您在您的机器上进行测试。我创建了一个文件,每个文件有 1M 行,结果时间为

time python something.py > /dev/null

是:

逐行:

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

读入内存:

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

我在尝试 2M 行时遇到 MemoryError,每行 300 个字符,但以上表明读入 RAM 会更快。

【讨论】:

【参考方案2】:

我在约 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

从结果中可以看出,一次读入整个文件要快得多,但你会冒着在文件中抛出 MemoryError 的风险太大。

【讨论】:

做一些关于 mmap 的阅读。通常是个好主意。即使内存是一个约束。【参考方案3】:

最好使用流式处理(逐行)构建您的程序,在这种情况下,您可以处理大量数据。 一般来说,最好实现读取,例如读取 100 行,然后处理它们,然后再加载 100 行。在低级别上,您只是使用大缓冲区并按大块读取原始文件。 如果您将所有内容加载到内存中 - 您可能会遇到像 @oseiskar 所写的内存错误

【讨论】:

【参考方案4】:

查看OP发布的示例代码,我认为对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()”。这在 *** 网站的其他地方都有说明。

另一个需要考虑的方面是文件系统缓冲。如果您对同一个文件运行相同的代码位,那么您将面临文件系统缓冲污染结果的风险。正如您所说,您有 200GB 的内存,足以缓冲足够的文件以影响运行结果。

您需要执行以下操作以确保测试结果干净:

1) 将大文件从已知来源复制到新文件名。 (文件系统必须不是 COW 文件系统。) 2)刷新文件系统缓存 3) 对文件运行第一个测试。 4)删除文件 5) 将文件从源重新复制到另一个新文件名。 6) 刷新文件系统缓存 7) 对新文件运行第二个测试。

这将为您提供更准确的文件加载时间测试。

如果您想一次将整个文件加载到内存中,filehandle.read(bytes to read) 是否会提供更快的块读取文件内容的方法?

无论哪种情况,供参考:

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

【讨论】:

以上是关于如果 RAM 不是问题,是逐行读取更快还是将所有内容读入 RAM 并访问它? - Python的主要内容,如果未能解决你的问题,请参考以下文章

VB6.0中如何实现逐行读入文本文件?

Python逐行读取文件内容

读取单列 CSV 文件的更快方法

AWK

awk

不是逐行读取文件