多处理不均匀分配作业 - Python

时间:2017-12-07 10:06:00

标签: python python-3.x multiprocessing python-multiprocessing

我正在尝试使用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})

1 个答案:

答案 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以获取示例。