Python:多处理,加载8/24核心

时间:2015-07-09 14:16:59

标签: python multiprocessing

我有一台 24个物理核的机器(至少我被告知)运行Debian:Linux 3.2.0-4-amd64 #1 SMP Debian 3.2.68-1+deb7u1 x86_64 GNU/Linux。这似乎是正确的:

usr@machine:~/$ cat /proc/cpuinfo  | grep processor
processor   : 0
processor   : 1
<...>
processor   : 22
processor   : 23

我尝试使用Python multiprocessing.pool.Pool加载所有内核时遇到了一些问题。我用Pool(processes=None);文档说如果提供cpu_count(),Python会使用None

唉,只有8个核心100%加载,其他核心仍处于空闲状态(我使用htop来监控CPU负载)。我以为我无法正确烹饪Pools并试图“手动”调用24个进程:

print 'Starting processes...'
procs = list()
for param_set in all_params:  # 24 items
    p = Process(target=_wrap_test, args=[param_set])
    p.start()
    procs.append(p)

print 'Now waiting for them.'
for p in procs:
    p.join()

我从我开始的流程中收到了24条“问候”消息:

Starting processes...
Executing combination: Session len: 15, delta: 10, ratio: 0.1, eps_relabel: 0.5, min_pts_lof: 5, alpha: 0.01, reduce: 500
< ... 22 more messages ... >
Executing combination: Session len: 15, delta: 10, ratio: 0.1, eps_relabel: 0.5, min_pts_lof: 7, alpha: 0.01, reduce: 2000
Now waiting for them.

仍然只加载了8个核心

enter image description here

我在这里看到,numpy,OpenBLAS和多核执行可能存在问题。这就是我启动代码的方式:

OPENBLAS_MAIN_FREE=1 python -m tests.my_module

在我完成所有进口之后:

os.system("taskset -p 0xff %d" % os.getpid())

所以,问题是:我应该怎样做才能在所有内核上实现100%的负载?这只是我糟糕的Python使用情况还是与多核计算机上的操作系统限制有关?

更新:另一个有趣的事情是htop输出中的一些不一致。如果你看一下上面的图像,你会发现CPU负载条下面的表显示了超过8个内核的30-50%负载,这与负载条所说的完全不同。然后,top似乎同意这些条:8个核心100%负载,其他闲置。

再次更新:

当我在所有导入后添加os.system("taskset -p 0xff %d" % os.getpid())行时,我在SO上使用了this rather popular post。我必须承认,当我这样做时,我并没有想太多,特别是在阅读之后:

  

在模块导入后粘贴此行,我的示例现在在所有核心上运行

我是个简单的男人。我看到“像魅力一样”,我复制并粘贴。无论如何,在玩我的代码时,我最终删除了这一行。之后,我的代码开始在所有24个内核上执行“手动”Process启动方案。对于Pool场景,无论是否使用了关联技巧,都会出现同样的问题。

我不认为这是一个真正的答案'因为我不知道Pool的问题是什么,但至少我设法让所有核心都满载。谢谢!

2 个答案:

答案 0 :(得分:3)

即使你解决了这个问题,我也会尝试解释它以澄清这些想法。

对于我所读到的内容,numpy做了很多&#34;魔术&#34;提高性能。其中一个神奇的技巧是设置进程的CPU亲和力。

CPU亲和性是OS调度程序的优化。它基本上强制给定的进程始终在同一CPU核心上运行。

这可以提高性能,减少CPU缓存失效的次数,并增加参考局部性的好处。在高计算任务中,这些因素确实很重要。

我不喜欢numpy的事实是它隐含地做了所有这些。经常让开发人员感到困惑。

您的进程未在所有核心上运行的事实是由于numpy在导入模块时为父进程设置了亲和力。然后,当您生成新进程时,将继承亲和性,导致所有进程争用少数核心,而不是有效地使用所有可用的进程。

os.system("taskset -p 0xff %d" % os.getpid())命令指示操作系统在解决您问题的所有核心上设置亲和力。

如果你想看到它在Pool上工作,你可以做以下技巧。

import os
from multiprocessing import Pool


def set_affinity_on_worker():
    """When a new worker process is created, the affinity is set to all CPUs"""
    print("I'm the process %d, setting affinity to all CPUs." % os.getpid())
    os.system("taskset -p 0xff %d" % os.getpid())


if __name__ == '__main__':
    p = Pool(initializer=set_affinity_on_worker)
    ...

答案 1 :(得分:0)

use std::result::Result; fn match_value(vals: i32) -> Result<i32, i32> { match vals { 2 => Ok(1), _ => Err(0), } } fn main() { let values = vec![1, 2, 3, 2]; let matching = values .iter() .map(|name| match_value(*name)) .filter(|x| x.is_ok()) .collect::<Vec<_>>(); println!("{:?}", matching); } 中,os.system("taskset -p 0xff %d" % os.getpid())本质上是十六进制位掩码,对应于1111 1111.位掩码中的每个位对应一个CPU内核。位值1表示可以在相应的CPU内核上执行该过程。因此,要在24个核心上运行,您应该使用0xffffff而不是0xff的掩码。

正确的命令:

0xff