我有一台 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个核心:
我在这里看到,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
的问题是什么,但至少我设法让所有核心都满载。谢谢!
答案 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