【问题标题】:CPU idle when parallelising using Python multiprocessing使用 Python 多处理并行化时 CPU 空闲
【发布时间】:2020-12-16 20:44:16
【问题描述】:

我已经使用 Python 的 multiprocessing 将一个函数并行化到一个包含各种参数的列表上,并且每个进程都会在中途冻结。发生这种情况时,我在 Ubuntu 机器上检查top,然后是1,发现内核现在大部分都处于空闲状态(https://man7.org/linux/man-pages/man1/top.1.html)。

这是我的代码:

from multiprocessing import Pool, Queue


class Parallelisable:
    # Adapted from: https://stackoverflow.com/questions/41992810/python-multiprocessing-across-different-files
    def _apply_function(self, function, args_queue):
        while not args_queue.empty():
            args = args_queue.get()
            # Apply function to arguments
            function(*args)

    def parallelise(self, function, args_list):
        queue = Queue()
        for args in args_list:
            queue.put(args)
        pool = Pool(None, self._apply_function, (function, queue,))
        pool.close()  # signal that we won't submit any more tasks to pool
        pool.join()  # wait until all processes are done



if __name__ == '__main__':
    # Define data_dir, output_dir, filenames and some_frozenset here
    # data_dir and output_dir are strings
    # filenames is a list of strings
    # some_frozenset is a frozenset of strings

    Parallelisable().parallelise(some_function, [(filename, data_dir, output_dir, some_frozenset) for filename in filenames])

我怀疑这是由于死锁造成的。

我想出了这些可能的解释,但它们对我来说没有多大意义:

  1. Parallelisable 对象是共享资源,其中一个子进程在任何一个时间点获取锁,并防止在多个子进程中调用self._apply_function()。我认为情况并非如此,因为我有 2 个子进程同时运行。我猜这可以通过强制子进程到call execve using the multiprocessing spawn method来解决
  2. function in parallelise()_apply_function() 是共享资源,类似于上面的第 1 点
  3. 我的一些代码不在 __main__ 中,但我不认为这是一个问题,因为我在 Ubuntu 而不是 Windows 上运行
  4. 其中一个参数 (filename, data_dir, output_dir, some_frozenset) 不是线程安全的,因为前 3 个是不可变字符串,最后一个是一组不可变的不可变字符串,所以不应该是这种情况

我有什么遗漏的吗?

顺便说一句,我认为我可以像这样重写上面的代码:

from multiprocessing import Pool

def parallelise_v2(function, args_list):
    with Pool(None) as pool:  # Use the "spawn" method if I want to call execve
        pool.starmap_async(function, args_list)


if __name__ == '__main__':
    # Define data_dir, output_dir, filenames and some_frozenset here

    parallelise_v2(some_function, [(filename, data_dir, output_dir, some_frozenset) for filename in filenames])

【问题讨论】:

  • 在stackoverflow中获得更多编码问题答案的一个技巧是制作一个比运行更小的程序。然后,人们可以运行您的代码,而不是查看源代码并试图弄清楚发生了什么。使用 viztracer 检查这里发生的事情将是一个非常有趣的案例,但不是没有一些可执行代码。
  • 您的任务是否有可能在“大小”上不相等,所以您只有一个进程在完成其余的工作?或者您的任务是绑定在磁盘上还是等待网络资源或其他什么?您的所有进程都可以在不使用所有 CPU 的情况下运行。与上述评论相呼应,如果您提供了一个重现问题的可运行示例,我会进一步研究它。
  • 谢谢@minker,这看起来真的很有用,我会研究一下。如果脚本中途卡住,viztracer 还会输出 json/html 文件吗?我没有提供实际代码,因为它对特定于任务的文件执行某些操作,我将尝试获取一些复制情况的最小代码。
  • @Macattack 任务不受 IO 限制,否则它会显示来自top 命令的高“wa, IO-wait”值。相反,CPU 内核都接近 100% “id, idle”。我正在使用服务器,据我所知代码/CPU/文件应该在同一台机器上,所以它不应该是网络问题。
  • viztracer 会在脚本卡住时输出文件。只需 Ctrl-C 即可。请记住,当您使用 viztracer 时,您需要 --log_multiprocess 用于多处理库。

标签: python multiprocessing fork deadlock


【解决方案1】:

这似乎是因为子进程是内存密集型的,Python 默默地杀死了它们。父进程只是等待子进程返回。我使用dmesg -T| grep -E -i -m10 -B20 'killed process'检查了杀死进程的原因,其中-m指定要返回的最大匹配数,-B指定匹配“杀死进程”之前的行数。

有关更多可能的原因和故障排除提示,请查看问题描述和问题中的 cmets。它们包括一些需要注意的事情以及viztracer 的建议以追踪发生的事情。引用@minker:

viztracer 会在脚本卡住时输出文件。只需 Ctrl-C 即可。请记住,当您使用viztracer 时,您需要--log_multiprocess 才能获得multiprocessing 库。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-03
    • 1970-01-01
    • 2012-10-04
    相关资源
    最近更新 更多