我正在尝试读取和处理1000个文件,但遗憾的是,处理文件的时间大约是从磁盘读取文件的3倍,因此我希望在读取这些文件时处理这些文件(而我继续阅读其他文件)。
在一个完美的世界中,我有一个一次读取一个文件的生成器,我想将这个生成器传递给一个工作池,这些工作器在生成(缓慢)生成时处理生成器中的项目。
以下是一个例子:
def process_file(file_string):
...
return processed_file
pool = Pool(processes=4)
path = 'some/path/'
results = pool.map(process_file, (open(path+part,'rb').read() for part in os.listdir(path)))
上面代码的唯一问题是在池开始之前所有文件都被读入内存,这意味着我需要等待磁盘读取所有内容,并且还消耗大量内存。
答案 0 :(得分:6)
Pool.map
和Pool.map_async
list
ify the iterable
passed to them, so your generator will always be realized fully before processing even begins。
各种Pool.imap*
函数出现以将输入作为生成器进行处理,因此您可以更改:
results = pool.map(process_file, (open(path+part,'rb').read() for part in os.listdir(path)))
为:
# If you can process outputs one at a time, drop the list wrapper
# If you can process outputs without order mattering, imap_unordered will
# get you the best results
results = list(pool.imap(process_file, (open(path+part,'rb').read() for part in os.listdir(path))))
并且在处理之前得到相同的结果而不是啜饮,但是AFAICT,他们仍然会尽可能快地完全填充队列,这可能导致大量数据未完成且内存使用过多;除此之外,您将在一个过程中读取所有数据,然后通过IPC发送所有数据,这意味着您仍然主要在I / O上遇到瓶颈。
在您的位置,我将读取移动到任务本身(如果可以,请避免读取整个文件,按行或按块处理,而不是阅读整个事情一下子)。您可以获得并行读取,减少IPC,并且在前几个文件被处理之前,您不会冒险诋毁所有文件;你没有比工人更多的文件打开。所以最终结果如下:
def process_file(path):
with open(path, 'rb') as f:
file_string = f.read()
... same as before ...
return processed_file
pool = Pool(processes=4)
path = 'some/path/'
results = pool.imap(process_file, (os.path.join(path, part) for part in os.listdir(path)))
答案 1 :(得分:2)
您正在将文件读入父母的内存中,然后将有效负载转移到子级中。那效率很低。只发送文件名,让孩子们进行I / O操作。如果结果是您计划写入文件的一堆文本,那么也要在子文件中执行该操作。
map
通常会一次性发布大量工作,以减少池工作者的通信开销。这可能是你获得大记忆飙升的原因。只传递文件名解决了这个问题,但是如果工人之间的处理时间不均匀,那么设置一个小的chunksize仍然是有益的。
def process_file(filename):
with open(filename, 'rb') as fp:
file_string = fp.read()
...
return processed_file
pool = Pool(processes=4)
path = 'some/path/'
results = pool.map(process_file, path+part for part in os.listdir(path)), chunksize=1)