多处理pool.apply_async会消耗内存

时间:2019-09-14 16:23:51

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

用例:

  1. 可使用10台服务器(16核128GB RAM)处理20亿个参数组合
  2. 每个服务器使用 pool.apply_async()(Python版本3.7)处理2亿个组合
  3. 使总处理时间尽可能短

问题:

  1. Python吞噬了所有内存并引发错误“ RuntimeError:无法启动新线程”和“ OSError:[Errno 12]无法分配内存

我正在考虑将 .apply_async()方法替换为 .apply(),但是我想这会通过更改非阻塞模式变为阻塞模式。

有人可以帮助找到这种情况下的最佳解决方案(节省时间)吗?

我的代码:

exec_log = multiprocessing.Manager().list([0, ''])
lock = multiprocessing.Manager().Lock()
cores = multiprocessing.cpu_count()
pool = multiprocessing.Pool(processes=cores)

# Parameters a to j
for a in a_list: # a_list contains 2 elements
    for b in b_list: # b_list contains 2 elements
        for c in c_list: # c_list contains 5 elements
            for d in d_list: # d_list contains 10 elements
                for e in e_list: # e_list contains 10 elements
                    for f in f_list: # f_list contains 5 elements
                        for g in g_list: # g_list contains 20 elements
                            for h in h_list: # h_list contains 10 elements
                                for i in i_list: # i_list contains 10 elements
                                    for j in j_list: # j_list contains 10 elements
                                        pool.apply_async(prestart, (df, start_date, end_date, curr_date, 
                                                                    analysis_period, a, b, c, d, e,
                                                                    f, g, h, i, j, exec_log, lock))
pool.close()
pool.join()
logger.info(exec_log[1])

1 个答案:

答案 0 :(得分:0)

  

Q 在这种情况下,谁能帮助找到最佳解决方案(耗时最少)??

好的,让我们检查购物清单的可见部分:

1)
避免使用Lock()处理,鉴于任何形式的Lock()处理仍然存在,您希望对代码执行进行并行组织会突然变成一个阻塞状态一个接一个地重新编排的纯[SERIAL](让其余的都等待轮到它-不管有多少个上位的大型RAM上位服务器投入工作-都在等待轮到,大部分时间都尝试“抢” Lock()

2)
避免使用任何形式的共享,鉴于任何形式的共享资源仍然存在,您希望拥有的并行工作流程(再次)诉诸于等待任何此类共享资源从任何资源中释放出来的时刻。他人的使用,并且可能会开始被该过程所使用。

3)
避免任何过多的进程/内存实例化(除了您已经经历的RAM崩溃崩溃之外,对于HPC级并行问题解决方案来说,这些开销也非常高-两者都在

  • [TIME] -域 ...实例化附加开销...〜成千上万的{{​​1}}
  • [ns] -域 ... O / S以越来越大的规模以显着的附加成本来处理新的内存分配要求,远远超过〜成千上万的数量[SPACE]中的数据,最糟糕的情况是陷入虚拟内存内存交换窒息...再次,为移动数据存储块付出了相关的[ns]域成本(RAM副本的成本约为300)对于NUMA-CPU核心非本地RAM目的地+ -350 [ns] +基于数据量的I / O带宽驱动和可用RAM通道的可用性进一步限制了此类数据传输延迟。精心策划的O / S交换流(即您控制范围之外的交换流)的代价是〜1,000 x〜10,000 x恶化……并且在此期间将其他任何计算和读取/写入RAM的尝试置于最高优先地位来进行阻止时间,所以另一个原因是没人希望在计算过程中发生这些事情)

使用 [TIME] 将显示localhost O / S可以提供​​的所有选项,以消除(小规模缓解)不必要的过度RAM分配。

使用 multiprocessing.get_all_start_methods() 控件而不是上面使用的len( os.sched_getaffinity(0) ),将消除localhost过度订阅免费使用的CPU内核数量(副本数量也更少)的情况的“只是”-{multiprocessing.cpu_count()-RAM副本的副本,必须等待其调度程序排序的翻转,然后其RAM / CPU执行才能翻转...),如果O / S相似性映射策略限制了用户-使用所有平台的程序均指示硬件内核。


基于“令人敬畏的” [CONCURRENT] 的“外部”迭代器可能总是会得到改进,但是核心策略更为重要:

可以将代码重构为计算:

一种精简且注重性能的计算方法,可以使现实成本与支出后的实际净收益之间取得平衡。

许多“精打细算”的方法都无法将规模扩展到某些主要增长规模之上-首先,通过经历来自In-CACHE计算的驱逐罪,感受到实际成本-RAM(在教科书示例和演示中没有观察到),接下来是In-RAM数据流的成本,对于越来越大的数据大小,最后但并非最不重要的是来自多处理的天真期望的成本(其中,具体取决于操作系统和版本,可能会导致分配许多完整的python-session-replicas的世代附加成本,这可能会导致内存错误崩溃-就像上面发布的情况一样)

鉴于10台服务器分别具有16个内核和128 GB RAM,这一有前途的举措将在计算带有数据的 for 时测试python-proc的大小,而不会生成下一个服务器中的“工作人员”要多得多,直到他们全部都适合RAM(以避免交换),然后再创建一个消息传递/信号元层,以在该分布式工作池中协调许多工作的智能参数传递,使用任何高性能,低延迟工具(如ZeroMQ或nanomsg)的设计,并设计工作流程,以使您永远不会两次传递单个数据(因为参数传递的附加成本比O(n)差得多)系统和O / S属性),因此在性能驱动的系统中永远不要两次移动数据。


遵循这几个简单的规则并不便宜,但是没有更快的方法(免费提供的内容越少...