首页 > 解决方案 > 为什么通过多处理库进行并行编程比通常的编程需要更长的时间?

问题描述

编辑:我更改了三次函数并定义了我注意到这个问题的实际优化问题。我还根据收到的评论更改了生成块的方式。

我正在尝试并行化一些代码以减少计算时间,但执行代码的并行化版本比非并行化版本花费更长的时间。我将展示一个简单的例子:

import multiprocessing as mp
import time
import numpy as np
from cvxpy import *
import functools
from sklearn.datasets import load_boston

boston = load_boston()
x = boston.data
y = boston.target

def lm_l_solver(x, y, lambda1):
    n = x.shape[0]
    m = x.shape[1]
    lambda_param = Parameter(sign="positive")
    beta_var = Variable(m)
    lasso_penalization = lambda_param * norm(beta_var, 1)
    lm_penalization = (1/n) * sum_squares(y - x * beta_var)
    objective = Minimize(lm_penalization + lasso_penalization)
    problem = Problem(objective)
    # Solve optimization problem
    beta_sol_matrix = np.zeros((len(lambda1), 1, m))
    for i in range(len(lambda1)):
        lambda_param.value = lambda1[i]
        problem.solve(solver=CVXOPT)
        beta_sol = np.asarray(np.row_stack([b.value for b in beta_var])).flatten()
        beta_sol_matrix[i, :] = beta_sol
    beta_sol_matrix[np.abs(beta_sol_matrix) < 1e-4] = 0
    # Generate response
    response = dict(solution=beta_sol_matrix, lambda1=lambda1)
    return response

if __name__ == '__main__':
    vector = np.arange(1, 100, 1)
    start_time = time.time()
    chunks = np.array_split(vector, mp.cpu_count())
    pool = mp.Pool(processes=mp.cpu_count())
    results = pool.map(functools.partial(lm_l_solver, x, y), chunks)
    pool.close()
    pool.join()
    end_time_1 = time.time()
    results2 = lm_l_solver(x, y, vector)
    end_time_2 = time.time()
    print('Parallel programming took {} seconds'.format(round(end_time_1-start_time, 2)))
    print('Non parallel programming took {} seconds'.format(round(end_time_2 - end_time_1, 2)))

函数 lm_l_solver 接收数据矩阵 x、响应向量 y 和可能的 lambda 值向量,并为每个 lambda 值求解一个惩罚线性模型。

执行这段代码会生成以下输出:

Parallel programming took 5.28 seconds
Non parallel programming took 0.4 seconds

为什么会有这样的差异?“lm_l_solver”的并行版本比非并行版本花费了 13 倍的时间。我在这里做错了吗?

标签: pythonparallel-processingmultiprocessing

解决方案


精确复制有点困难,因为它取决于集群的配置。就我而言,无论我使用自己的 4 核 CPU 还是小型 24 核服务器,并行化的行为都不会变得更糟。

无论如何,罪魁祸首是使用 BLAS 的求解器 CVXOPT 已经是多线程的。通过尝试并行化您的代码,您正在与该线性代数库竞争。为了证明我的观点,我强迫 BLAS 只使用一个线程。在这种情况下,多处理可以显示出一些优势:

$ python solver.py
Parallel programming took 2.0 seconds
Non parallel programming took 2.73 seconds

$ OMP_NUM_THREADS=1 python solver.py
Parallel programming took 0.57 seconds
Non parallel programming took 2.73 seconds

设置 OMP_NUM_THREADS=1 基本上会关闭 OpenMP 多线程,因此您的每个 Python 进程都保持单线程;并且 BLAS 也使用一个线程。

对于您的应用程序,您必须平衡线程数(使用 OMP_NUM_THREADS)和进程数(mp.Pool(processes=24)例如)。

阅读参考资料

再现性

requirements.txt使用 Python 2.7.5:

numpy
cvxpy==0.4.11
sklearn
cvxopt

推荐阅读