为什么在导入numpy后多进程只使用一个核心?

139

我不确定这是否更多地算作操作系统问题,但我想问一下这里是否有人从Python的角度能提供一些见解。

我一直试图使用joblib并行化一个CPU密集型的for循环,但我发现每个工作进程都被分配到同一个核心,而没有性能提升。

以下是一个非常简单的例子...

from joblib import Parallel,delayed
import numpy as np

def testfunc(data):
    # some very boneheaded CPU work
    for nn in xrange(1000):
        for ii in data[0,:]:
            for jj in data[1,:]:
                ii*jj

def run(niter=10):
    data = (np.random.randn(2,100) for ii in xrange(niter))
    pool = Parallel(n_jobs=-1,verbose=1,pre_dispatch='all')
    results = pool(delayed(testfunc)(dd) for dd in data)

if __name__ == '__main__':
    run()

...以下是我在运行此脚本时在 htop 中看到的:

htop

我正在运行 Ubuntu 12.10(3.5.0-26)在一台有4个内核的笔记本电脑上。显然,joblib.Parallel 正在为不同的工作进程生成单独的进程,但是否有任何方法可以使这些进程在不同的内核上执行?


1
很抱歉,那里没有答案,但听起来像是同样的问题。 - NPE
同时 https://dev59.com/0mw05IYBdhLWcg3w_Guw - NPE
并且 https://dev59.com/I2jWa4cB1Zd3GeqPut3K - NPE
1
这个问题还存在吗?我正在尝试使用Python 3.7重新创建它,并使用multiprocessing.Pool()导入numpy,它正在使用所有线程(应该如此)。只是想确保这个问题已经被解决了。 - Jared Nielsen
这是同一个问题吗?https://joblib.readthedocs.io/en/latest/parallel.html#avoiding-over-subscription-of-cpu-resources "一些第三方库(例如numpy使用的BLAS运行时)在内部管理线程池以执行它们的计算。... joblib告诉支持的第三方库在由“loky”后端管理的工作程序中使用有限数量的线程...自joblib 0.14以来,还可以使用parallel_backend函数的inner_max_num_threads参数以编程方式覆盖默认线程数。" - endolith
3个回答

168

通过进一步搜索,我找到了答案这里

事实证明,某些Python模块(numpyscipytablespandasskimage等)在导入时会干扰核心关联性。据我所知,这个问题似乎是由它们链接多线程OpenBLAS库引起的。

一种解决方法是使用以下命令重置任务亲和力:

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

在导入模块之后粘贴这行代码,我的示例现在可以在所有内核上运行:

htop_workaround

迄今为止,我的经验是,这似乎对numpy的性能没有任何负面影响,尽管这可能与机器和任务有关。

更新:

还有两种方法可以禁用OpenBLAS本身的 CPU 亲和力重置行为。在运行时,您可以使用环境变量OPENBLAS_MAIN_FREE(或 GOTOBLAS_MAIN_FREE)来实现,例如

OPENBLAS_MAIN_FREE=1 python myscript.py

或者,如果您正在从源代码编译OpenBLAS,则可以通过编辑Makefile.rule文件并添加该行来永久禁用它。

NO_AFFINITY=1

谢谢,你的解决方案解决了问题。我有一个问题,我有相同的代码但在两台不同的机器上运行结果不同。这两台机器都是Ubuntu 12.04 LTS,Python 2.7,但只有一台出现了这个问题。你有任何想法为什么吗? - iampat
两台机器都安装了使用OpenMPI构建的OpenBLAS。 - iampat
2
虽然这是一个旧帖子,但如果有其他人遇到了这个问题,我也曾经遇到过类似的问题,确实与OpenBLAS库有关。请参见此处,了解两种可能的解决方法和一些相关讨论。 - Gabriel
2
另一种设置CPU亲和性的方法是使用psutil - 0 _
2
@JHG 这是OpenBLAS的问题,而不是Python的问题,所以我看不出Python版本会有任何影响的原因。 - ali_m
显示剩余5条评论

35

Python 3现在公开了方法,可以直接设置亲和性

>>> import os
>>> os.sched_getaffinity(0)
{0, 1, 2, 3}
>>> os.sched_setaffinity(0, {1, 3})
>>> os.sched_getaffinity(0)
{1, 3}
>>> x = {i for i in range(10)}
>>> x
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> os.sched_setaffinity(0, x)
>>> os.sched_getaffinity(0)
{0, 1, 2, 3}

1
错误 > AttributeError: 模块 'os' 没有属性 'sched_getaffinity',Python 3.6 - Vadim
4
从相关文档中得知:它们仅适用于某些Unix平台。 - BlackJack
2
我遇到了同样的问题,不过我在代码开头集成了以下这行代码 os.system("taskset -p 0xff %d" % os.getpid()) ,但是还是不能完全使用所有的CPU。 - rajeshcis
2
我在一个集群上遇到了同样的问题。任何在计算节点上运行的Python进程都只会使用1个核心,即使我的代码原则上可以使用更多的核心,而且我已经请求了大约20个核心。对我来说,将import os和os.sched_setaffinity(0,range(1000))添加到我的Python代码中解决了这个问题。 - Georg

15

1
Python在Ubuntu上运行,这意味着它可以在Windows和其他操作系统上无障碍地工作。是吗? - Mast

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接