Многопроцессорная обработка медленнее, чем последовательная обработка в Windows (но не в Linux) - PullRequest
0 голосов
/ 23 сентября 2018

Я пытаюсь распараллелить for loop, чтобы ускорить мой код, поскольку все операции циклической обработки независимы.После онлайновых руководств кажется, что стандартная библиотека multiprocessing в Python - хорошее начало, и я начал работать с базовыми примерами.

Однако для моего реального случая использования я обнаружил, что параллельная обработка (использование двухъядерного компьютера) на самом деле немного (<5%) медленнее, когда работает на Windows.Выполнение того же кода в Linux, однако, приводит к увеличению скорости параллельной обработки на ~ 25% по сравнению с последовательным выполнением. </p>

Из документации я считаю, что это может быть связано с отсутствием в Windows функции fork ()Это означает, что каждый раз процесс должен быть инициализирован заново.Тем не менее, я не совсем понимаю это и задаюсь вопросом, может ли кто-нибудь подтвердить это, пожалуйста?

В частности,

-> Означает ли это, что весь код в вызывающем файле Python запускается для каждогопараллельный процесс в Windows, даже инициализация классов и импорт пакетов?

-> Если это так, можно ли этого избежать, если каким-то образом передать копию (например, с помощью глубокой копии) класса в новые процессы?

-> Существуют ли какие-либо советы / другие стратегиидля эффективного распараллеливания дизайна кода как для Unix, так и для Windows.

Мой точный код длинный и использует много файлов, поэтому я создал пример структуры в стиле псевдокода, которая, как мы надеемся, покажет проблему.

# Imports
from my_package import MyClass
imports many other packages / functions

# Initialization (instantiate class and call slow functions that get it ready for processing)
my_class = Class()
my_class.set_up(input1=1, input2=2)

# Define main processing function to be used in loop
def calculation(_input_data):
    # Perform some functions on _input_data
    ......
    # Call method of instantiate class to act on data
    return my_class.class_func(_input_data)

input_data = np.linspace(0, 1, 50)
output_data = np.zeros_like(input_data)

# For Loop (SERIAL implementation)
for i, x in enumerate(input_data):
    output_data[i] = calculation(x)

# PARALLEL implementation (this doesn't work well!)
with multiprocessing.Pool(processes=4) as pool:
    results = pool.map_async(calculation, input_data)
    results.wait()
output_data = results.get()

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

1 Ответ

0 голосов
/ 24 сентября 2018
В операционных системах

NT отсутствует примитив UNIX fork.Когда создается новый процесс, он запускается как пустой процесс.Родитель обязан проинструктировать новый процесс о том, как выполнить начальную загрузку. API-интерфейсы

Python multiprocessing абстрагируют процесс создания процесса, пытаясь дать то же чувство при запуске fork, forkserver и spawnметоды.

Когда вы используете spawn метод запуска, это то, что происходит под капотом.

  1. Создается пустой процесс
  2. Пустой процесс запускает брендновый интерпретатор Python
  3. Интерпретатору Python предоставляется MFA (аргументы функции модуля), заданные вами через инициализатор класса Process
  4. Интерпретатор Python загружает данный модуль, разрешая все операции импорта
  5. Функция target просматривается внутри модуля и вызывается с заданными значениями args и kwargs

Вышеуказанный поток имеет мало последствий.

Как вы заметили, это намного более сложная операция по сравнению с fork.Вот почему вы заметили такую ​​разницу в производительности.

Поскольку модуль импортируется с нуля в дочерний процесс, все побочные эффекты импорта выполняются заново.Это означает, что константы, глобальные переменные , декораторы и инструкции первого уровня будут выполнены снова.

С другой стороны, инициализация, выполненная во время выполнения родительского процесса, не будетраспространяться на ребенка.См. этот пример.

Вот почему в документации multiprocessing они добавили специальный параграф для Windows в Руководство по программированию .Я настоятельно рекомендую прочитать Руководство по программированию, так как оно уже содержит всю необходимую информацию для написания переносимого кода мультиобработки.

...