【问题标题】:Difference between a "worker" and a "task" for concurrent.futures.ProcessPoolExecutorconcurrent.futures.ProcessPoolExecutor 的“工作者”和“任务”之间的区别
【发布时间】:2021-12-07 18:59:11
【问题描述】:

我在 python 上运行了一个“令人尴尬的并行”问题,我想我可以使用 concurrent.futures 模块来并行化这个计算。我之前成功地做到了这一点,这是我第一次尝试在比我的笔记本电脑更强大的计算机上做到这一点。这台新机器有 32 核 / 64 线程,而我的笔记本电脑只有 2/4。

我正在使用来自concurrent.futures library 的ProcessPoolExecutor 对象。我将max_workers 参数设置为10,然后在一个循环中一个接一个地提交我所有的工作(其中可能有100 个)。模拟似乎有效,但即使经过一番激烈的谷歌搜索,也有一些我不明白的行为。我在 Ubuntu 上运行它,所以我使用 htop 命令来监控我的处理器。我看到的是:

  1. 创建了 10 个进程。
  2. 每个进程请求 > 100% 的 CPU 功率(例如,高达 600%)
  3. 还创建了一大堆进程。 (我认为这些是“任务”,而不是进程。当我按 SHIFT+H 时,它们就会消失。)
  4. 最令人担忧的是,所有处理器的假脱机似乎都达到了 100%。 (我说的是终端顶部的“均衡器条”:

Screenshot of htop

我的问题是——如果我只派出 10 名工人,为什么我的所有处理器似乎都在以最大容量使用?我的工作理论是,我调用的 10 名工人是“保留的”,而其他处理器只是跳进来帮忙......如果其他人要运行其他东西并要求一些处理能力(但不包括我要求的 10 名工人),我的其他任务将退后并归还。但是……这并不是“创建 10 个流程”对我的直观感觉。

如果你想要一个 MWE,我的代码大致是这样的:

def expensive_function(arg):
    a = sum(list(range(10 ** arg)))
    print(a)
    return a


def main():
    import concurrent.futures
    from random import randrange

    with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:
        # Submit the tasks
        futures = []
        for i in range(100):
            random_argument = randrange(5, 7)
            futures.append(executor.submit(expensive_function, random_argument))

        # Monitor your progress:
        num_results = len(futures)
        for k, _ in enumerate(concurrent.futures.as_completed(futures)):
            print(f'********** Completed {k + 1} of {num_results} simulations **********')


if __name__ == '__main__':
    main()

【问题讨论】:

  • 我要猜测 concurrent.futures 创建了十个进程并在它们之间/向它们分配数百个任务,并且操作系统在内核之间分配工作负载以它认为合适的方式。
  • 那么,如果操作系统只是要将代码分发到所有内核,那么保留大量工作人员的意义何在?单个进程有“600%”的 CPU 消耗是什么意思?
  • ??尝试使用 max_workers 参数,看看性能或完成时间是否会发生变化。

标签: python linux concurrency htop


【解决方案1】:

由于GIL,单个进程在给定时间只能有 1 个线程执行 python 字节码,所以如果你有 10 个进程,你应该有 10 个线程(因此核心)在给定时间执行 python 字节码,但是这不是完整的故事。

expensive_function 不明确,python 可以创建 10 个工作进程,因此在给定时间(+ 主进程)只能有 10 个内核执行 python 代码(由于 GIL),但是,如果 expensive_function 是使用外部 C 模块(不必遵守 GIL)进行某种多线程处理,然后 10 个进程中的每一个都可以有 Y 个线程并行工作,因此您总共将拥有 10*Y 个内核在给定时间使用,例如,您的代码可能在 10 个进程中的每一个上运行 6 个外部线程,总共有 60 个线程在 60 个内核上同时运行。

然而这并不能真正回答你的问题,所以主要的答案是,workers 是在给定时间可以执行 python 字节码的进程(核心)的数量(特别强调“python 字节码”),其中tasks 是将由您的工作人员执行的任务总数,当任何工作人员完成手头的任务时,它将开始另一个任务。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-22
    • 1970-01-01
    • 2016-11-29
    • 1970-01-01
    • 2018-03-31
    • 2012-11-27
    • 1970-01-01
    • 2012-10-01
    相关资源
    最近更新 更多