Почему важно защитить основной цикл при использовании joblib.Parallel?

Документы joblib содержат следующее предупреждение:

В Windows важно защитить основной цикл кода, чтобы избежать рекурсивного порождения подпроцессов при использовании joblib.Parallel. Другими словами, вы должны писать такой код:

import ....

def function1(...):
    ...

def function2(...):
    ...

... if __name__ == '__main__':
    # do stuff with imports and functions defined about
    ...

Никакой код не должен выполняться за пределами блоков «if __name__ == ‘__main__’», только импорт и определения.

Первоначально я предполагал, что это было просто для предотвращения случайного странного случая, когда функция, переданная joblib.Parallel, рекурсивно вызывает модуль, что означало бы, что это в целом хорошая практика, но часто ненужная. Однако мне непонятно, почему это может быть риском только для Windows. Кроме того, этот ответ, по-видимому, указывает на то, что отказ от защиты основного цикла привел к тому, что код работал в несколько раз медленнее, чем в противном случае. есть для очень простой нерекурсивной задачи.

Из любопытства я запустил сверхпростой пример досадно параллельного цикла из документации joblib без защиты основного цикла в окне Windows. Мой терминал был заспамлен следующей ошибкой, пока я не закрыл его:

ImportError: [joblib] Attempting to do parallel computing without protecting your import on a system that does not suppo
rt forking. To use parallel-computing in a script, you must protect you main loop using "if __name__ == '__main__'". Ple
ase see the joblib documentation on Parallel for more information

Мой вопрос: как насчет реализации joblib в Windows, требующей защиты основного цикла в каждом случае?

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

Наконец, я хочу отметить, что это чисто академический; Я понимаю, почему обычно рекомендуется писать свой код таким образом, и будет продолжать делать это независимо от joblib.


person Joe    schedule 09.04.2015    source источник


Ответы (2)


Это необходимо, потому что в Windows нет fork(). Из-за этого ограничения Windows необходимо повторно импортировать ваш модуль __main__ во все дочерние процессы, которые он порождает, чтобы воссоздать родительское состояние в дочернем. Это означает, что если у вас есть код, порождающий новый процесс на уровне модуля, он будет рекурсивно выполняться во всех дочерних процессах. Защита if __name__ == "__main__" используется для предотвращения повторного выполнения кода в области модуля в дочерних процессах.

В Linux это не обязательно, потому что у него действительно есть fork(), что позволяет разветвлять дочерний процесс, сохраняющий то же состояние, что и родительский, без повторного импорта модуля __main__.

person dano    schedule 09.04.2015
comment
Интересно, спасибо! Должен сказать, что если бы я разрабатывал операционную систему, я бы подумал, что возможность разветвления процессов была бы довольно простой и желательной... но я думаю, что это просто еще одно любопытное ограничение Windows. - person Joe; 10.04.2015

На случай, если кто-то наткнется на это в 2021 году: из-за новой бэкэнд-локи, используемой joblib > 0.12, защита основного цикла for больше не требуется. См. https://joblib.readthedocs.io/en/latest/parallel.html

person burtphil    schedule 08.01.2021