编程语言
首页 > 编程语言> > Python readlines()用法和有效的阅读练习

Python readlines()用法和有效的阅读练习

作者:互联网

我有一个问题是在文件夹中解析1000个文本文件(每个文件大约3000行,大小约400KB).我确实用readlines读过它们,

   for filename in os.listdir (input_dir) :
       if filename.endswith(".gz"):
          f = gzip.open(file, 'rb')
       else:
          f = open(file, 'rb')

       file_content = f.readlines()
       f.close()
   len_file = len(file_content)
   while i < len_file:
       line = file_content[i].split(delimiter) 
       ... my logic ...  
       i += 1  

这对我输入的样本(50,100个文件)完全没问题.当我在整个输入上运行超过5K的文件时,所花费的时间远不及线性增量.我计划进行性能分析并进行Cprofile分析.当输入达到7K文件时,更多文件以指数级增长而达到更差的速率所花费的时间.

这是readlines的累计时间,
第一 – > 354个文件(来自输入的样本)和
第二 – > 7473个文件(整个输入)

 ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 354    0.192    0.001    **0.192**    0.001 {method 'readlines' of 'file' objects}
 7473 1329.380    0.178  **1329.380**    0.178 {method 'readlines' of 'file' objects}

因此,我的代码所花费的时间不会随着输入的增加而线性缩放.我读了一些关于readlines()的文档说明,其中人们声称这个readlines()将整个文件内容读入内存,因此与readline()或read()相比通常会占用更多内存.

我同意这一点,但是垃圾收集器是否应该在循环结束时自动从内存中清除加载的内容,因此在任何时刻我的内存应该只包含当前处理文件的内容?但是,这里有一些问题.有人可以对这个问题有所了解.

这是readlines()的固有行为还是我对python垃圾收集器的错误解释.很高兴知道.

此外,建议一些替代方法,以内存和时间效率的方式做同样的事情. TIA.

解决方法:

简短版本是:The efficient way to use readlines() is to not use it. Ever.

I read some doc notes on readlines(), where people has claimed that this readlines() reads whole file content into memory and hence generally consumes more memory compared to readline() or read().

readlines()的文档明确保证它将整个文件读入内存,并将其解析为行,并构建一个完整的字符串列表.

但是read()的文档同样保证它将整个文件读入内存,并构建一个字符串,这样做无济于事.

除了使用更多内存之外,这也意味着在读取整个内容之前,您无法完成任何工作.如果您以最天真的方式交替阅读和处理,您将受益于至少一些流水线操作(由于操作系统磁盘缓存,DMA,CPU管道等),因此您将在下一批时处理一个批处理正在阅读.但是,如果您强制计算机读取整个文件,然后解析整个文件,然后运行您的代码,您只能获得整个文件的一个重叠区域,而不是每个读取重叠工作的一个区域.

您可以通过以下三种方式解决此问题:

>在readlines(sizehint),read(size)或readline()周围写一个循环.
>只需将文件用作惰性迭代器而不调用其中任何一个.
> mmap该文件,它允许您将其视为一个巨大的字符串,而无需先读取它.

例如,这必须立即读取所有foo:

with open('foo') as f:
    lines = f.readlines()
    for line in lines:
        pass

但这一次只能读取大约8K:

with open('foo') as f:
    while True:
        lines = f.readlines(8192)
        if not lines:
            break
        for line in lines:
            pass

而且这一次只能读取一行 – 虽然Python允许(并且将)选择一个漂亮的缓冲区大小以使事情更快.

with open('foo') as f:
    while True:
        line = f.readline()
        if not line:
            break
        pass

这将完成与前一个完全相同的事情:

with open('foo') as f:
    for line in f:
        pass

与此同时:

but should the garbage collector automatically clear that loaded content from memory at the end of my loop, hence at any instant my memory should have only the contents of my currently processed file right ?

Python没有对垃圾收集做出任何此类保证.

CPython实现碰巧使用refcounting for GC,这意味着在你的代码中,一旦file_content被反弹或消失,巨大的字符串列表及其中的所有字符串将被释放到空闲列表中,这意味着相同的记忆可以再次用于你的下一次传球.

但是,所有这些分配,复制和解除分配都不是免费的 – 不执行它们比执行它们要快得多.

最重要的是,让你的字符串分散在一大块内存中,而不是一遍又一遍地重复使用相同的小块内存会损害你的缓存行为.

另外,虽然内存使用量可能是恒定的(或者更确切地说,是最大文件大小的线性,而不是文件大小的总和),但第一次扩展它的mallocs的速度将是最慢的你做的事情(这也使得进行性能比较变得更加困难).

总而言之,这就是我编写程序的方式:

for filename in os.listdir(input_dir):
    with open(filename, 'rb') as f:
        if filename.endswith(".gz"):
            f = gzip.open(fileobj=f)
        words = (line.split(delimiter) for line in f)
        ... my logic ...  

或者可能:

for filename in os.listdir(input_dir):
    if filename.endswith(".gz"):
        f = gzip.open(filename, 'rb')
    else:
        f = open(filename, 'rb')
    with contextlib.closing(f):
        words = (line.split(delimiter) for line in f)
        ... my logic ...

标签:readlines,python-2-6,python,performance,memory
来源: https://codeday.me/bug/20190916/1807211.html