在多处理中如何将 CPU 内核分配给 python 进程?
Posted
技术标签:
【中文标题】在多处理中如何将 CPU 内核分配给 python 进程?【英文标题】:How do CPU cores get allocated to python processes in multiprocessing? 【发布时间】:2018-10-04 05:50:12 【问题描述】:假设我在多核 CPU(比如 4)上运行多个 python 进程(不是线程)。 GIL 是进程级别的,因此特定进程中的 GIL 不会影响其他进程。
我的问题是,一个进程中的 GIL 是只占用 4 个核心中的一个核心,还是会占用所有 4 个核心?
如果一个进程同时锁定所有内核,那么多处理应该不会比 python 中的多线程更好。如果不是,内核如何分配给各个进程?
作为观察,在我的系统中,它是 8 个核心(4*2,因为 超线程),当我运行单个 CPU 绑定进程时,CPU 使用率 8 个内核中有 4 个上升。
简化:
在 4 核 CPU 上运行的 4 个 python 线程(在一个进程中)将比执行相同工作的单个线程花费更多时间(考虑到该工作完全受 CPU 限制)。 4 个不同的流程做这么多的工作是否会减少将近 4 倍的时间?
【问题讨论】:
【参考方案1】:Python 没有对bind processes or threads to cores 做任何事情;它只是把事情留给操作系统。当您生成一堆独立的进程(或线程,但这在 Python 中更难做到)时,操作系统的调度程序将快速有效地让它们分布在您的内核中,而无需您或 Python 做任何事情(除非非常糟糕的病态例)。
GIL 在这里不相关。我稍后会谈到,但首先让我们解释一下是相关的。
您没有 8 个内核。你有 4 个核心,每个核心都是hyperthreaded。
现代内核具有大量“超标量”容量。通常,在管道中排队的指令不够独立,无法充分利用该容量。超线程的作用是允许内核在发生这种情况时从第二条管道中获取其他指令,这些指令几乎可以保证是独立的。但它只允许这样做,而不是要求这样做,因为在某些情况下(CPU 通常可以比你更好地决定)缓存局部性的成本会比并行性的收益更糟糕。
因此,根据您运行的实际负载,使用四个超线程内核,您可能会获得 800% 的 CPU 使用率,或者您可能只会获得 400%,或者(通常)介于两者之间。
我假设您的系统配置为向用户空间报告 8 个内核而不是 4 个内核,因为这是默认设置,并且您至少有 8 个进程或具有默认 proc 计数和至少 8 个任务的池 — 显然,如果这些都不是真的,你不可能得到 800% 的 CPU 使用率……
我还假设您没有使用显式锁、其他同步、Manager
对象或任何其他将序列化您的代码的东西。如果这样做,显然您无法获得完全的并行性。
而且我还假设您没有使用(可变)共享内存,例如每个人都写入的multiprocessing.Array
。这可能会导致缓存和页面冲突,几乎与显式锁定一样糟糕。
那么,GIL 是怎么回事?好吧,如果您在一个进程中运行多个线程,并且它们都受 CPU 限制,并且它们都花费大部分时间运行 Python 代码(而不是花费大部分时间运行释放GIL),一次只能运行一个线程。你可以看到:
在单核上始终保持 100%,而其余为 0%。 两个或多个内核之间 100% 乒乓球,而其余的为 0%。 两个或多个内核之间 100% 乒乓球,而其余内核为 0%,但有一些明显的重叠,其中两个内核同时超过 0%。最后一个可能看起来像并行性,但实际上并非如此——这只是切换开销变得可见。但是您不是在运行多个线程,而是在运行单独的进程,每个进程都有自己完全独立的 GIL。这就是为什么您会看到 100% 的四个核心,而不是只有一个。
【讨论】:
***文章+1。但这并不能完全回答我的问题。像 numpy 这样的库通过在 C 中运行可以高效多线程的外部代码来绕过 GIL 限制。这里我只运行一个python进程。 @sprksh 答案明确表示“与花费大部分时间运行释放 GIL 的 numpy 操作相反”。你觉得还有什么需要说的? @sprksh 另外,如果您的实际问题是关于 numpy 的多线程,您为什么要问如何在多处理中分配内核,并从“假设我正在运行多个 python 进程(而不是线程)…”? @sprksh 不管怎样,试着猜测你真正想知道的,而不是你问的:如果你的线程受 CPU 限制,并且运行 Python 代码而不是 GIL 发布的 C 代码,那么是的,切换到multiprocessing
可以让您获得近 4 倍的加速。但当然不能保证——如果你有锁或共享数据,或者切换到多处理需要腌制巨大的对象来回传递,或者你正在运行一堆小任务,每个任务使用单独的进程而不是池等,您最终可能只会增加比您获得的并行性更多的开销。
这最后一条评论是对我的问题的更好回答,它带来了另一个问题。因此,您是说考虑到单个进程,GIL 可能会或可能不会根据操作系统处理它的方式以及可用的内核数来控制多个内核。此外,并行运行的 4 个进程可以(不一定总是)执行 4 个不同的 CPU 绑定任务,因为每个进程占用 1 个内核?【参考方案2】:
进程到 CPU/CPU 内核的分配由操作系统处理。
【讨论】:
是的,但如果我需要查看 8 个内核中的 4 个,我可以将其分配给 R 中的多处理。以上是关于在多处理中如何将 CPU 内核分配给 python 进程?的主要内容,如果未能解决你的问题,请参考以下文章