【问题标题】:Multiprocessing pool.apply_async eats up memory多处理 pool.apply_async 占用内存
【发布时间】:2019-09-14 16:23:51
【问题描述】:

用例:

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

问题:

  1. Python 耗尽所有内存并抛出错误“RuntimeError: can't start new thread”和“OSError: [Errno 12] Cannot allocate memory

我正在考虑将 .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])

【问题讨论】:

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


    【解决方案1】:

    谁能帮助找到最好的解决方案(花费最少的时间)这种情况?

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

    1)
    避免使用Lock()-handling,因为任何形式的Lock()-ing 仍然存在,您希望拥有的平行- 代码执行的组织内爆回到阻塞状态重新编排的纯-[SERIAL] 一个接一个(让所有其余的等待轮到他们 - 无论放置多少大型 RAM-on-steriods 服务器在工作中——所有人都在等待轮到他们,大部分时间都在努力“抓住”Lock()

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

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

    • [TIME]-domain ... 实例化附加开销 ... ~ 大量的 [ns] 和 in
    • [SPACE]-domain ...新的内存分配需求由 O/S 以不断增长的规模以显着的附加成本来处理,远远超过 ~ 大量 [ns],最糟糕的是陷入虚拟内存内存交换窒息......再次,为移动数据内存块支付相关的[TIME]域成本(内存中的副本成本约为 NUMA-300-350 [ns] CPU 内核非本地 RAM 目标 + 基于数据量的 I/O 带宽驱动和空闲 RAM 通道可用性进一步限制了此类数据传输延迟。在陷入交换的情况下,O/S 协调(即外部您的控制域)交换流的成本约为 1,000 x ~ 10,000 x 更糟......并且在此期间通过优先考虑任何其他计算和读/写 RAM 的尝试来阻止,所以另一个原因没有人会曾经希望这些在计算过程中发生)

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

    使用 len( os.sched_getaffinity(0) ) 控件而不是上面使用的multiprocessing.cpu_count(),将消除 localhost 过度订阅免费使用的 CPU 内核的数量(也有更少的“just”副本-[CONCURRENT]-RAM-copies,必须等待其调度程序排序的轮次,然后才能轮到它们的 RAM/CPU 执行...),如果 O/S 关联映射策略限制用户程序使用所有平台指定的硬件内核。


    基于“糟糕”for的“外部”迭代器可能总能得到改进,但核心策略更为重要:

    可以将代码重构为 计算:

    一种干净且注重性能的 计算方法可以平衡实际成本与人们在花费这些成本后将享受的净性能收益。

    许多“syntax-in-mind”方法无法在某些principal不断增长的规模之上进行扩展 - 首先,通过体验 In-CACHE 计算中的驱逐罪来感受真正的成本 - -RAM(在教科书示例和演示中没有观察到),接下来是内存数据流的成本,对于越来越大的数据大小,最后但并非最不重要的是,来自多处理的幼稚期望的成本(其中,取决于操作系统和版本,可能会引入分配许多完整python会话副本的生成附加成本,这可能会导致内存错误崩溃——就像上面发布的案例一样)

    鉴于 10 台服务器每台有 16 个内核和 128 GB RAM,在计算 prestart() 时,概念上的有希望的举措将测试 python-proces 的大小,并且下一次生成不会更多"工作人员”,而不是直到它们都放入 RAM 中(以避免交换),接下来创建一个消息传递/信号元层来协调在这个分布式工作人员池中的许多工作的智能参数传递,使用任何高性能、低延迟的工具,如 ZeroMQ 或 nanomsg,并设计工作流,以便您永远不会两次传递单个数据(因为参数传递的附加成本比 O(n) 更糟,由于系统和 O/S 属性),因此切勿在以性能为动力的系统中将一条数据移动两次。


    遵循这几条简单的规则并不便宜,但没有更快的方法(免费的越少......)

    【讨论】:

      猜你喜欢
      • 2016-07-21
      • 1970-01-01
      • 2015-06-08
      • 1970-01-01
      • 2014-09-29
      • 1970-01-01
      • 1970-01-01
      • 2020-10-30
      • 1970-01-01
      相关资源
      最近更新 更多