CPU 使用 Python 多处理并行化时空闲

CPU idle when parallelising using Python multiprocessing

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

这是我的代码:

from multiprocessing import Pool, Queue


class Parallelisable:
    # Adapted from: 
    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. 来解决
  3. function in parallelise() and _apply_function() 是共享资源,类似于上面第1点
  4. 我的一些代码不在 __main__ 中,但我不认为这是一个问题,因为我 运行 在 Ubuntu 而不是 Windows
  5. 其中一个参数 (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])

好像是因为子进程占用内存大,Python默默杀掉了。父进程只是等待子进程return。我使用 dmesg -T| grep -E -i -m10 -B20 'killed process' 检查了终止进程的原因,其中 -m 指定了与 return 的最大匹配数,而 -B 指定了匹配“终止进程”之前的行数。

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

viztracer will output the file if the script is stuck. Just Ctrl-C out of it. Just remember you need --log_multiprocess for multiprocessing library when you use viztracer.