Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为什么多核处理比单核慢?使用joblib或dask会有所不同吗? 问题_Python_Python Multiprocessing_Dask_Numba_Joblib - Fatal编程技术网

Python 为什么多核处理比单核慢?使用joblib或dask会有所不同吗? 问题

Python 为什么多核处理比单核慢?使用joblib或dask会有所不同吗? 问题,python,python-multiprocessing,dask,numba,joblib,Python,Python Multiprocessing,Dask,Numba,Joblib,我试图优化一些计算,这些计算会导致所谓的令人尴尬的并行计算,但我发现使用python的多处理包实际上会减慢速度 我的问题是:我是做错了什么,还是并行化实际上减慢了速度有一个内在的原因?是因为我在用麻木吗?像joblib或dak这样的其他软件包会有很大的不同吗? 有很多类似的问题,其中的答案总是开销大于时间节省,但所有这些问题都倾向于围绕非常简单的函数,而我本来希望嵌套循环的东西更适合并行化。我还没有找到joblib、多处理和dask之间的比较 我的职能 我有一个函数,它将一维numpy数组作为s

我试图优化一些计算,这些计算会导致所谓的令人尴尬的并行计算,但我发现使用python的
多处理
包实际上会减慢速度

我的问题是:我是做错了什么,还是并行化实际上减慢了速度有一个内在的原因?是因为我在用麻木吗?像joblib或dak这样的其他软件包会有很大的不同吗?

有很多类似的问题,其中的答案总是开销大于时间节省,但所有这些问题都倾向于围绕非常简单的函数,而我本来希望嵌套循环的东西更适合并行化。我还没有找到joblib、多处理和dask之间的比较

我的职能 我有一个函数,它将一维numpy数组作为shape n的参数,并输出一个shape(n x t)的numpy数组,其中每一行都是独立的,即输出的第0行仅取决于输入的第0项,第1行取决于第1项,等等。类似这样的情况:

基础计算通过优化,可将速度提高多个数量级

玩具示例-结果 我不能分享确切的代码,所以我想出了一个玩具的例子。在
my_fun\u numba
中定义的计算实际上是不相关的,它只是一些非常平庸的数字运算,以保持CPU繁忙

以玩具为例,在我的电脑上得到的结果就是这些,它们与我用实际代码得到的结果非常相似

如您所见,将输入数组拆分为不同的块,并将它们发送到multiprocessing.pool实际上比在单个内核上使用numba要慢。

我试过的 我在
numba.jit
decorator中尝试了
cache
nogil
选项的各种组合,但差别很小

我已经用PyCharm分析了代码(不是timeit.Timer部分,只是一次运行),如果我正确理解了输出,似乎大部分时间都花在等待池上

按时间排序:

按自己的时间排序:

玩具示例-代码
并行运行时间基本不变这一事实表明,您只是没有尝试足够大的输入,以产生将数据分发到多个并行实例并从多个并行实例中收集结果的开销。@chepner但6核上的开销似乎约为2秒,12核上的开销约为4秒。这不是太多了吗?每次分发到一个或多个核心时都必须发生什么事情?为什么需要这么长时间?这是因为一些numba初始化吗?最有可能的是,主进程在启动时向彼此的核心发送数据,而不是使用共享内存。此外,由于内核太少,单个内核负责启动所有其他内核,而不是像风扇一样,在每一步中都会初始化成倍数量的内核。看来my_fun_numba的速度太快了,不需要将其并行化。如果你花5分钟来计算my_fun_numba,那将有助于并行化,因为这样做可以克服分发数据和收集结果的开销。这是正常的。我不清楚为什么要使用多处理并行化numba函数<对于numba.prange(n)中的r,code>:和设置
parallel=True
足以获得多线程解决方案,其开销要少得多。但在这样一个小问题上,即使这样也未必有益。
import numpy as np
import pandas as pd
import multiprocessing
from multiprocessing import Pool
import numba
import timeit


@numba.jit(nopython = True, nogil = True, cache = True)
def my_fun_numba(x):
    dim2 = 10
    out = np.empty((len(x), dim2))
    n = len(x)
    for r in range(n):   
        for c in range(dim2):
            out[r,c] = np.cos(x[r]) ** 2 + np.sin(x[r]) ** 2
    return out

def my_fun_non_numba(x):
    dim2 = 10
    out = np.empty((len(x), dim2))
    n = len(x)
    for r in range(n):   
        for c in range(dim2):
            out[r,c] = np.cos(x[r]) ** 2 + np.sin(x[r]) ** 2
    return out


def my_func_parallel(inp, func, cpus = None):
    if cpus == None:
        cpus = max(1, multiprocessing.cpu_count() - 1)
    else:
        cpus = cpus
        
    inp_split = np.array_split(inp,cpus)
    pool = Pool(cpus)    
    out = np.vstack(pool.map(func, inp_split) )    
    pool.close()
    pool.join()
    return out

if __name__ == "__main__":
    inputs = np.array([100,10e3,1e6] ).astype(int)
    res = pd.DataFrame(index = inputs, columns =['no paral, no numba','no paral, numba','numba 6 cores','numba 12 cores'])
    
    r = 3
    n = 1

    
    for i in inputs:
        my_arg = np.arange(0,i)

        
        res.loc[i, 'no paral, no numba'] = min(
            timeit.Timer("my_fun_non_numba(my_arg)", globals=globals()).repeat(repeat=r, number=n)
            )
        
        res.loc[i, 'no paral, numba'] = min(
            timeit.Timer("my_fun_numba(my_arg)", globals=globals()).repeat(repeat=r, number=n)
            )
        
        res.loc[i, 'numba 6 cores'] = min(
            timeit.Timer("my_func_parallel(my_arg, my_fun_numba, cpus = 6)", globals=globals()).repeat(repeat=r, number=n)
            )
        
        res.loc[i, 'numba 12 cores'] = min(
            timeit.Timer("my_func_parallel(my_arg, my_fun_numba, cpus = 12)", globals=globals()).repeat(repeat=r, number=n)
            )