为什么在使用Python多处理池时会有空闲的工作线程?
我将一个非常大的文本文件分解成更小的块,并对这些块执行进一步的处理。对于本例,让为什么在使用Python多处理池时会有空闲的工作线程?,python,parallel-processing,python-multiprocessing,Python,Parallel Processing,Python Multiprocessing,我将一个非常大的文本文件分解成更小的块,并对这些块执行进一步的处理。对于本例,让text\u chunks成为一个列表列表,每个列表包含一段文本。文本块的元素长度从~50到~15000不等。类ProcessedText存在于代码中的其他位置,并根据提供给它的文本进行大量后续处理和数据分类。使用如下代码将不同的文本块并行处理为ProcessedText实例: def do_things_to_text(a, b): #pull out necessary things for Proces
text\u chunks
成为一个列表列表,每个列表包含一段文本。文本块的元素长度从~50到~15000不等。类ProcessedText
存在于代码中的其他位置,并根据提供给它的文本进行大量后续处理和数据分类。使用如下代码将不同的文本块并行处理为ProcessedText
实例:
def do_things_to_text(a, b):
#pull out necessary things for ProcessedText initialization and return an instance
print('Processing {0}'.format(a))
return ProcessedText(a, b)
import multiprocessing as mp
#prepare inputs for starmap, pairing with list index so order can be reimposed later
pool_inputs = list(enumerate(text_chunks))
#parallel processing
pool = mp.Pool(processes=8)
results = pool.starmap_async(do_things_to_text, pool_inputs)
output = results.get()
代码成功执行,但似乎在代码运行时,作为池
的一部分创建的一些工作进程随机处于空闲状态。在代码执行时,我在top
中跟踪内存使用情况、CPU使用情况和状态
在开始时,所有8个工作进程都被占用(状态“R”在top
中,CPU使用率为非零),当text\u块中的约20个条目完成后,工作进程开始变化很大。有时,只有一个工作进程正在运行,而其他进程处于“S”状态,CPU使用率为零。我还可以从打印的输出语句中看到,do\u things\u to\u text()
被调用的频率较低。到目前为止,我还无法确定进程开始空闲的原因。还有很多条目需要处理,因此它们闲置会导致时间效率低下
我的问题是:
为什么这些工作进程处于空闲状态
有没有更好的方法来实现多处理
来防止这种情况
编辑以添加:
我进一步描述了这个问题。从我在do_things\u to_text()
中打印的索引中可以清楚地看出,多处理是将作业总数按每十个索引划分为线程。因此,我的控制台输出显示作业0、10、20、30、40、50、60、70同时提交(8个进程)。有些作业完成得比其他作业快,因此您可能会在看到作业1完成之前看到作业22已完成
在第一批线程完成之前,所有进程都处于活动状态,没有空闲。但是,当该批处理完成且作业80启动时,只有一个进程处于活动状态,其他7个进程处于空闲状态。我还没有确认,但我相信在80系列完成之前,它会一直保持这种状态。以下是一些提高内存利用率的建议:
我不知道如何创建文本块
,但最终在池输入中得到了8GB的字符串。理想情况下,您应该有一个生成器函数,例如,make_text_chunks
,该函数生成以前包含text_chunks
iterable的单个“text chunks”(如果text_chunks
已经是这样一个生成器表达式,则您都已设置好)。这样做的目的不是一次性创建所有8GB的数据,而是仅在需要数据时创建。使用此策略,您不能再使用Pool
方法starmap\u asynch
;我们将使用。与startmap\u asynch
不同,此方法将以chunksize
块的形式迭代提交作业,您可以在结果可用时对其进行处理(尽管这似乎不是问题)
文本文件有多大?提交了多少作业,即len(池输入)
?进程空闲多长时间?@Booboo文本文件大小会有所不同,但对于我目前正在开发的示例,大约为8GB。pool_输入目前为301个元素,但在一般应用中也可能有所不同。我认为这些进程可能会空闲5-10分钟,而一个进程在不空闲时通常会在1分钟左右完成。我也有一些新的发现,我将用它们来更新原来的问题;我只能提出一些更好地利用记忆的建议。他们是否会解决你所有的问题是一个大问号。@Booboo欢迎所有的建议!一些评论:(1)请参阅下面我的答案中的compute\u chunksize
函数。这就是starmap\u asynch
将用于根据池的大小和pool\u输入的大小将pool\u输入分解为“块”的函数。例如,给定pool\u输入中的池大小为8和300个元素,将计算chunksize
值10。因此,它将把pool_inputs
的前10个元素放在第一个进程的输入队列上,接下来的10个元素放在第二个进程的输入队列上。这就是为什么您会看到初始作业打印为0、10、20等(更多…)谢谢!对于这个问题,这当然值得考虑,但不幸的是,在当前迭代中,必须将块读入内存,因为我必须以固定的间隔拆分原始文本文件,然后重新组合所有内容,以使其按分隔符拆分。我会记住,在将来重新写入时,只需将文本文件读入内存,并从一开始进行分隔符拆分,这将允许我imap
。
def make_text_chunks():
# logic goes here to generate the next chunk
yield text_chunk
def do_things_to_text(t):
# t is now a tuple:
a, b = t
#pull out necessary things for ProcessedText initialization and return an instance
print('Processing {0}'.format(a))
return ProcessedText(a, b)
import multiprocessing as mp
# do not turn into a list!
pool_inputs = enumerate(make_text_chunks())
def compute_chunksize(n_jobs, poolsize):
"""
function to compute chunksize as is done by Pool module
"""
if n_jobs == 0:
return 0
chunksize, remainder = divmod(n_jobs, poolsize * 4)
if remainder:
chunksize += 1
return chunksize
#parallel processing
# number of jobs approximately
# don't know exactly without turning pool_inputs into a list, which would be self-defeating
N_JOBS = 300
POOLSIZE = 8
CHUNKSIZE = compute_chunksize(N_JOBS, POOLSIZE)
with mp.Pool(processes=POOLSIZE) as pool:
output = [result for result in pool.imap(do_things_to_text, pool_inputs, CHUNKSIZE)]