首页 > 解决方案 > Joblib 嵌套并行执行不使用可用内核

问题描述

通过在外部语句上使用 n_jobs>1 的嵌套 Parallel 语句,嵌套 Parallel 函数似乎是从 1 个线程而不是 4 个线程可用的受限资源。即考虑以下高度简化的脚本来重现该问题:

from joblib import Parallel, delayed
import numpy as np

def parallel_in_parallel_test(i):
    a = np.ones((1000,1000))
    for j in range(2000):
        a *= np.random.randn(1000,1000)
    return a.sum()

def parallel_in_parallel_wrapper(j, n_threads=4):
    out2 = Parallel(n_jobs=n_threads)(delayed(parallel_in_parallel_test)(i) for i in range(100))
    return np.array(out2).sum()

out = Parallel(n_jobs=3)(delayed(parallel_in_parallel_wrapper)(j, n_threads=4) for j in range(100))

我原本预计 3 个并行进程会产生 4 个进程,从而产生 400% 的 CPU 使用率,即每个“父进程”有 4 个内核/线程。但是,这 4 个“子进程”不是并行运行的。每个“父进程”使用 100% 的 CPU 而不是 400%(只有一个核心/线程)

考虑以下来自 htop 的屏幕截图: htop 屏幕截图显示活动线程数量少和非活动线程数量多

我可以用简约的环境重现该问题:

conda create --name py39 python=3.9
conda activate py39
conda install numpy joblib

我的操作系统:Ubuntu 18.04.5 LTS

对于我的特定代码,非嵌套方法是不可行的,因为嵌套并行位于类的方法中。

任何建议如何正确利用可用资源?这只是一个简单的错误吗?

标签: python-3.xmultithreadingparallel-processingnestedjoblib

解决方案


显然,更改嵌套语句中使用的后端的 inner_max_num_threads 限制是可行的——我仍然不清楚为什么首先需要这样做。

from joblib import Parallel, delayed, parallel_backend
import numpy as np

def parallel_in_parallel_test(i):
    a = np.ones((1000,1000))
    for j in range(2000):
        a *= np.random.randn(1000,1000)
    return a.sum()

def parallel_in_parallel_wrapper(j, n_threads=4):
    with parallel_backend("loky", inner_max_num_threads=n_threads):
        out2 = Parallel(n_jobs=n_threads)(delayed(parallel_in_parallel_test)(i) for i in range(100))
    return np.array(out2).sum()

out = Parallel(n_jobs=3)(delayed(parallel_in_parallel_wrapper)(j, n_threads=4) for j in range(100))

推荐阅读