我正在尝试使用Pool均匀地在16个处理器之间分配我的所有作业。我注意到最初产生了16个进程。几秒钟后,只有2个进程执行少量作业的所有剩余作业。无论我增加了多少负载,似乎都没有减少任何正在处理它的进程。最终,只有1或2个进程完成剩余的工作。
以下是我的代码中的多处理代码段。
c_size = len(sampled_patterns) / (cpu_count() -1)
pool = Pool(processes=cpu_count() -1)
works = [(pattern, support_set, hit_rates) for pattern,support_set in sampled_patterns.items()]
pool.starmap(get_hit_rules,works, chunksize=int(c_size))
是否仍然使用所有16个处理器来最大化并行化?谢谢!
编辑! 这就是分配任务的方式。计数器以pid为键,任务数量为值。
Counter({30179: 14130, 30167: 13530, 30169: 12900, 30173: 12630, 30165: 12465, 30177: 12105, 30163: 11820, 30175: 11460, 30161: 10860, 30181: 10725, 30183: 9855, 30157: 8695, 30159: 6765, 30171: 4860, 30155: 1770})
答案 0 :(得分:1)
好的,我会将此作为答案进行扩展。
multiprocessing.Pool
的全部意义在于它产生了许多进程,然后以首先自由优先的方式将工作分配给它们。这意味着如果您要处理n
个项目,并且池中有p
个进程,则会选择p
(如果已定义p * chunksize
,则为chunksize
项目数量并将每个项目发送到单独的流程进行处理。一旦进程完成处理项目并被有效释放,如果仍有未处理的项目,池将获取下一个项目,将其发送到已释放的进程,依此类推,直到没有剩余项目为止。这可确保您生成的流程的最佳化,而无需自行管理分发。
这也意味着multiprocessing.Pool
不适合所有情况。在您的情况下,根据显示的代码,您希望将迭代器均匀地分配到固定数量的进程,因此池只是一个开销 - 一旦进程完成,将不再有数据要分发。如果您只想分割数据并将每个块发送到不同的进程,则只需:
import multiprocessing
if __name__ == "__main__": # always guard your multiprocessing code
cores = max(multiprocessing.cpu_count() - 1, 1) # ensure at least one process
works = [(p, s, hit_rates) for p, s in sampled_patterns.items()]
chunk_size = (len(works) + cores - 1) // cores # rough chunk size estimate
processes = [] # a simple list to hold our process references
for i in range(cores):
work_set = works[i*chunk_size:(i+1)*chunk_size]
process = multiprocessing.Process(target=get_hit_rules, args=(work_set,))
process.start()
processes.append(process)
results = [process.join() for process in processes] # get the data back
这将完全按照您的尝试进行操作 - 启动cpu_count()
进程并发送每个进程(粗略地说,最后一个进程将获得更少的平均数据)数据块大小均匀以这种方式,您可以同时并行处理所有数据。
当然,如果您的数据太大,而且您在评论中另外澄清了这将最终无法管理,那么您可以恢复到multiprocessing.Pool
以将可管理的数据块发送到生成的流程连续处理。此外,构建works
列表也毫无意义 - 为什么要构建包含数十亿项目的列表,而这些项目已经包含sampled_patterns
字典中的数据?
为什么不在sampled_patterns
dict中发送单个项目而不构建中间列表,以便将其映射到multiprocessing.Pool
?为此,您只需要创建某种迭代器切片器并将其提供给multiprocessing.Pool.imap
,然后让池内部管理其余部分,所以:
import multiprocessing
def patterns_slicer(patterns, size, hit_rates):
pos = 0 # store our current position
patterns = patterns.items() # use the items iterator
while pos < len(patterns):
yield [(p, s, hit_rates) for p, s in patterns[pos:pos+size]]
pos += size
if __name__ == "__main__": # always guard your multiprocessing code
cores = max(multiprocessing.cpu_count() - 1, 1) # ensure at least one process
pool = multiprocessing.Pool(processes=cores)
# lets use chunks of 100 patterns each
results = pool.imap(get_hit_rules, patterns_slicer(sampled_patterns, 100, hit_rates))
当然,multiprocessing.Pool.imap
做了很多预测,所以如果您的原始数据太大,或者您想要使用大块,您可能需要考虑使用just-in实现自己的imap
时间数据检索。请查看this answer以获取示例。