среда, 22 марта 2017 г.

Python joblib и GIL: ретроспектива после сессии дебага

Есть такая отличная библиотечка для параллелльного запуска одной функции с разными параметрами в параллельных потоках - joblib.

Сегодня я столкнулся с тем, что некоторые библиотечки питона могут быть зависимы от логики GIL, из-за чего при параллельном выполнении метода вы можете получать System Error: broken pipe, хотя тот же код в один поток будет работать.

В моем случае все осложнялось ещё и тем, что код, который падал, был внутри фикстуры для автоматических тестов, запускаемых с pytest. И без pytest проблема не воспроизводилась, а дебажить работу функции, запускаемой через joblib.Parallel внутри pytest фикстуры это то ещё удовольствие.

Самое печальное это то, что несколько часов поиска в Интернете не давали ничего вменяемого, как будто с этой проблемой никто не сталкивался раньше, поэтому я решил написать эту статью, чтобы, если кто-то ещё столкнется с подобной проблемой, она могла вас навести на правильное решение.

Решение кроется в использовании альтернативного метода для параллельного запуска кода - multi-threading, который, конечно же, зависит от GIL, но и ваш код не падает, как в случае с multiprocessing.

Чтобы намекнуть joblib использовать multi-threading, вам необходимо указать параметр backend=“threading”, вот так:
from joblib import Parallel, delayed

results = Parallel(n_jobs=200, backend='threading')(
                delayed(your_function)(i, parameter2)
                for i in xrange(10000))
Здесь results будет содержать список возвращаемых функцией your_function значений, your_function - функция, которую мы запустим в 200 тредов, i и parameter2 - параметры, которые будут переданы в качестве аргументов функции your_function (они не обязательные, просто как пример). Значение n_jobs определяет количество тредов, которое будет создано для запуска функции, backend='threading' сообщает интерпретатору что надо создавать именно треды, а не отдельные процессы.

Подробнее об этой библиотечке и этом параметре можно почитать здесь: http://pythonhosted.org/joblib/generated/joblib.Parallel.html