14

如果 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
4

4 回答 4

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}

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

于 2013-02-23T10:52:20.050 回答
8

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

time python something.py > /dev/null

是:

逐行:

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

读入 RAM:

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

尝试使用 2M 行,每行 300 个字符时出现 MemoryError,但以上表明读入 RAM 会更快。

于 2013-02-23T10:32:21.093 回答
6

查看 OP 发布的示例代码,我认为对 Python 正在做什么存在误解。

IE:

“逐行阅读”

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

上面看起来像是逐行读取。但是,Python 将此解释为“将尽可能多的文件读入内存,然后将每个文件作为一行处理”。所以实际上,上面的 for 循环将所有内容读入内存。

“读入 RAM”

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(bytes to read) 是否可能提供一种更快的块读取文件内容的方法?

无论哪种情况,供参考:

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

于 2013-04-03T17:52:05.260 回答
0

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

于 2013-04-01T12:09:07.380 回答