Почему параллельное программирование через многопроцессорную библиотеку занимает больше времени, чем обычное программирование? - PullRequest
0 голосов
/ 13 июня 2018

РЕДАКТИРОВАТЬ : я изменил кубическую функцию и вместо этого определил реальную проблему оптимизации, в которой я заметил эти проблемы.Я также изменил способ генерации чанков, основываясь на полученных комментариях.

Я пытаюсь распараллелить некоторый код, чтобы сократить время вычислений, но выполнение распараллеленной версии кода занимает больше времени, чем непараллельная версия.Я покажу простой пример:

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 и вектор возможных значений лямбды и решает штрафную линейную модель для каждого из значений лямбды.

Выполнение этого фрагмента кода приводит к следующему выводу:

Parallel programming took 5.28 seconds
Non parallel programming took 0.4 seconds

Почему такая разница?Распараллеленная версия "lm_l_solver" заняла в 13 раз больше времени, чем непараллельная версия.Я что-то здесь не так делаю?

Ответы [ 2 ]

0 голосов
/ 20 июня 2018

Точно воспроизвести немного сложно, так как это зависит от конфигурации вашего кластера.В моем случае я не получаю намного худшего параллелизма, независимо от того, использую ли я свой 4-ядерный процессор или небольшой 24-ядерный сервер.

В любом случае, виновником является ваш решатель, CVXOPT, который использует BLAS, уже многопоточный.Пытаясь распараллелить ваш код, вы конкурируете с этой библиотекой линейной алгебры.Чтобы доказать свою точку зрения, я заставил 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
0 голосов
/ 20 июня 2018

многопроцессорная обработка используется для функций, связанных с процессором.Вы не можете действительно сравнить производительность по задачам, которая длится 0,5 секунд в одном процессе.Ваш сценарий тестирования должен быть обновлен.

При использовании многопроцессорной обработки возникает много накладных расходов по сравнению с одним процессом.Python должен порождать новые процессы, хэндовер задачи, сообщать результаты обратно и т. Д.

Numpy фактически выполняет внешний C-код за кулисами, который оптимизируется.Это означает, что он будет выполняться очень быстро поочередно, на одном процессоре.

Расходы на диспетчеризацию доминируют над фактическим временем вычисления в вашем вопросе .

Вам следует увеличить ввод данных, чтобы Numpy потребовалось около 1-2 минут для завершения одного процесса, а затем перейти к многопроцессорному выполнению для сравнения.

Кроме того, вы можете получить лучшие результаты в многопроцессорном режиме, есливы бы использовали imap_unordered вместо map, если порядок не важен.

Есть хороший blogpost , который делает некоторыесравнение между многопоточностью и многопроцессорностью, и это также касается операций Numpy.Я думаю, что вы могли бы найти это полезным.

...