来自joblib的多处理不并行化?

时间:2018-03-20 09:53:30

标签: python scikit-learn python-3.6 joblib

由于我从python3.5移动到3.6,使用joblib的并行计算并没有减少计算时间。 以下是安装了librairies的版本:   - python:3.6.3   - joblib:0.11   - numpy:1.14.0

基于一个众所周知的例子,我在下面给出了一个示例代码来重现问题:

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

def square_int(i):
    return i * i

ndata = 1000000 
ti = time.time()
results = []    
for i in range(ndata):
    results.append(square_int(i))

duration = np.round(time.time() - ti,4)
print(f"standard computation: {duration} s" )

for njobs in [1,2,3,4] :
    ti = time.time()  
    results = []
    results = Parallel(n_jobs=njobs, backend="multiprocessing")\
        (delayed(square_int)(i) for i in range(ndata))
    duration = np.round(time.time() - ti,4)
    print(f"{njobs} jobs computation: {duration} s" )

我得到了以下产品:

  • 标准计算:0.2672 s
  • 1个工作量计算:352.3113 s
  • 2个工作量计算:6.9662 s
  • 3个工作量计算:7.2556 s
  • 4个工作量:7.097秒

当我将ndata的数量增加10倍并删除1核计算时,我得到了这些结果:

  • 标准计算:2.4739 s
  • 2个工作量计算:77.8861 s
  • 3个工作量计算:79.9909 s
  • 4个工作量计算:83.1523 s

有没有人知道我应该调查哪个方向?

1 个答案:

答案 0 :(得分:1)

我认为主要原因是并行产生的开销超过了收益。换句话说,您的square_int太简单了,无法通过并行获得任何性能改进。 square_int非常简单,以至于在进程之间传递输入和输出可能比执行功能square_int花费更多的时间。

我通过创建一个square_int_batch函数来修改了您的代码。尽管它比串行实现更多,但它大大减少了计算时间。

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

def square_int(i):
    return i * i

def square_int_batch(a,b):
    results=[]
    for i in range(a,b):
        results.append(square_int(i))
    return results

ndata = 1000000 
ti = time.time()
results = []    
for i in range(ndata):
    results.append(square_int(i))

# results = [square_int(i) for i in range(ndata)]

duration = np.round(time.time() - ti,4)
print(f"standard computation: {duration} s" )

batch_num = 3
batch_size=int(ndata/batch_num)

for njobs in [2,3,4] :
    ti = time.time()  
    results = []
    a = list(range(ndata))
#     results = Parallel(n_jobs=njobs, )(delayed(square_int)(i) for i in range(ndata))
#     results = Parallel(n_jobs=njobs, backend="multiprocessing")(delayed(
    results = Parallel(n_jobs=njobs)(delayed(
        square_int_batch)(i*batch_size,(i+1)*batch_size) for i in range(batch_num))
    duration = np.round(time.time() - ti,4)
    print(f"{njobs} jobs computation: {duration} s" )

计算时间为

standard computation: 0.3184 s
2 jobs computation: 0.5079 s
3 jobs computation: 0.6466 s
4 jobs computation: 0.4836 s

一些其他建议可以帮助减少时间。

  1. 在您的特定情况下,使用列表理解results = [square_int(i) for i in range(ndata)]代替for循环,它更快。我测试了。
  2. batch_num设置为合理的大小。该值越大,开销越大。在我的情况下,batch_num超过1000时,它开始变得非常慢。
  3. 我使用默认后端loky而不是multiprocessing。至少在我看来,它速度稍快。

从其他一些SO问题中,我读到,多处理对于 cpu-heavy 任务很有用,对此我没有正式的定义。您可以自己探索。