Python joblib и случайное блуждание - производительность [CONCURRENT] -процессного планирования

Вот мой код python-3.6 для имитации одномерного отраженного случайного блуждания с использованием модуля joblib для генерации 400 реализаций одновременно для рабочих K на кластерной машине Linux.

Замечу, однако, что время выполнения для K=3 хуже, чем для K=1, и что время выполнения для K=5 еще хуже!

Кто-нибудь может найти способ улучшить мое использование joblib?

from math import sqrt
import numpy as np
import joblib as jl
import os

K = int(os.environ['SLURM_CPUS_PER_TASK'])

def f(j):
    N = 10**6
    p = 1/3
    np.random.seed(None)
    X = 2*np.random.binomial(1,p,N)-1   # X = 1 with probability p
    s = 0                               # X =-1 with probability 1-p
    m = 0 
    for t in range(0,N):
        s = max(0,s+X[t])
        m = max(m,s)
    return m

pool = jl.Parallel(n_jobs=K)
W = np.asarray(pool(jl.delayed(f)(j) for j in range(0,400)))
W      

Не могли бы вы указать время, которое вы получите для различных значений K?

Stefan Collier 14.06.2018 17:55

Для K = 1 CPUTime = 00: 10: 52. Для K = 3 CPUTime = 00: 11: 36. Для K = 5 CPUTime = 00: 12: 58.

S. Finch 14.06.2018 18:18

@ S.Finch, не могли бы вы также опубликовать фактическое изображение hwloc / lscpu системы NUMA?

user3666197 14.06.2018 18:25

@ S.Finch. Не могли бы вы сотрудничать с людьми, которые потратили свое время и знания, чтобы помочь вам? Решение в значительной степени зависит от того, как доступные аппаратные ресурсы используются и планируются в потоке обработки, где планирование чисто-[SERIAL]стандартные инструменты Python могут сделать весь блок из 400-RandomWalks x 1E6-drunken-sailor-steps менее чем в 1.45 [s] вместо ваших опубликованных ~ 11-13 [мин] (!). Если целью было обучать студентов на {R | MATLAB | Julia | Stata} с добавлением подхода Python, опубликуйте карту lstopo тестируемого NUMA-кластера

user3666197 15.06.2018 13:09

Боюсь, что кластеры NUMA и Python GlobalInterpreterLock намного превосходят уровень навыков моих студентов (на элементарном семинаре по статистическому программированию). Спасибо за вашу доброту и опыт.

S. Finch 16.06.2018 15:37

@ S.Finch Приятно слышать, что вы цените опыт и усилия, поддерживая как ваш первоначальный интерес, так и ваши последующие замечания + дополнительные вопросы. Правила сообщества StackOverflow устанавливают, что члены сообщества должны нажимать «Голосовать за» на содержании, которое было интересным, вдохновляющим или имело какое-либо другое положительное значение (щелкните треугольник [^] в верхнем левом углу как в вопросах, так и в ответах, а также в комментариях за такое вознаграждение и указание положительной стоимости) вместо того, чтобы вводить «спасибо» в комментарий. Лучший ответ должен быть отмечен как [Принято]. Так работает StackOverflow. Это честно, не правда ли?

user3666197 23.06.2018 13:18
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
6
410
1

Ответы 1

@ user3666197 написал очень хороший ответ о накладных расходах с большим количеством полужирного текста;) Однако я хочу обратить ваше внимание, что когда вы запускаете свой код с K = 1, вы делаете только одно случайное блуждание. При K = 3 или 5 вы совершаете 3 или 5 случайных блужданий одновременно (кажется). Таким образом, вам нужно умножить время выполнения K = 1 на 3 или 5, чтобы получить время выполнения, которое требуется для выполнения той же работы. Я думаю, это время выполнения будет намного больше, чем у вас.

Ну, чтобы дать полезный ответ, а не просто примечание (OP прямо в комментариях). Кажется, модуль multiprocessing - лучший выбор. Вот ваш код

from math import sqrt
import numpy as np

from multiprocessing import Pool
import os

K = int(os.environ['NTASK'])


def f(j):
    N = 10**6
    p = 1./3.
    np.random.seed(None)
    X = 2*np.random.binomial(1,p,N)-1   # X = 1 with probability p
    s = 0                               # X =-1 with probability 1-p
    m = 0 
    for t in range(0,N):
        s = max(0,s+X[t])
        m = max(m,s)
    return m

pool = Pool(processes=K) 
print pool.map(f, xrange(40))

и производительность

$ time NTASK=1 python stof.py                                                                                                
[21, 19, 17, 17, 18, 16, 17, 17, 19, 19, 17, 16, 18, 16, 19, 22, 20, 18, 16, 17, 17, 16, 18, 18, 17, 17, 19, 17, 19, 19, 16, 16, 18, 17, 18, 18, 19, 20, 16, 19]

real    0m30.367s
user    0m30.064s
sys     0m 0.420s

$ time NTASK=2 python stof.py                                                                                                
[18, 16, 16, 17, 19, 17, 21, 18, 19, 21, 17, 16, 15, 25, 19, 16, 20, 17, 15, 19, 17, 16, 20, 17, 16, 16, 16, 16, 17, 23, 17, 16, 17, 17, 19, 16, 17, 16, 19, 18]

real    0m13.428s
user    0m26.184s
sys     0m 0.348s

$ time NTASK=3 python stof.py 
[18, 17, 16, 19, 17, 18, 20, 17, 21, 16, 16, 16, 16, 17, 22, 18, 17, 15, 17, 19, 18, 16, 15, 16, 16, 24, 20, 16, 16, 16, 22, 19, 17, 18, 18, 16, 16, 19, 17, 18]

real    0m11.946s
user    0m29.424s
sys     0m 0.308s

$ time NTASK=4 python stof.py
[16, 19, 17, 16, 19, 17, 17, 16, 18, 22, 16, 21, 16, 18, 15, 16, 20, 17, 22, 17, 16, 17, 17, 20, 22, 21, 17, 17, 16, 17, 19, 16, 19, 16, 16, 18, 25, 21, 19, 18]

real    0m 8.206s
user    0m26.580s
sys     0m 0.360s

Отрицательно, сэр. В любом случае процесс def-ed всегда зацикливается точно 1E6 раза «внутри» любого из порожденных joblib400-прогулки, независимо от того, как они в конечном итоге были распределены по путям выполнения кода K == { 1, 3, 5, ... }. Плюс конечно, Oveheads имеют значение здесь :o)

user3666197 14.06.2018 18:37

@ user3666197, конечно, полностью с вами согласен! Накладные расходы и, в случае OP, время распространения шлама имеют значение. Но забавно думать, что, когда мы распределяем процессы, мы ожидаем, что за одно и то же время будет выполнено только умножение работы. Насколько я помню, в библии параллельного программирования есть очень приятное замечание об этом предположении ДАД

rth 14.06.2018 19:05

Для учебных пособий я успешно распараллелил свой код случайного блуждания, используя специальные модули в R, Matlab, Julia и Stata. (Под «успешно» я имею в виду, что совершенно очевидно, что 20 работников выполняют как минимум в 10 раз больше работы, чем 1 работник за тот же интервал времени.) Возможно ли такое внутреннее распараллеливание нет в Python? Какой модуль предпочтительнее, чем "joblib"? Я бы с удовольствием увидел конкретную ревизию моего кода, которая оказалась успешной. Спасибо!

S. Finch 14.06.2018 19:28

@rth Для ясности OP явно упомянул питон-3.6, это означает, что ваш код будет генерировать NameError: name 'xrange' is not defined, в то время как python-2.x будет иметь проблемы с памятью после выделения всех блоков памяти экземпляров range():o) >>> tio.run/##K6gsycjPM/7/Py2/SCFTITNPoaIoMS89VUPB0EBB04pLAQgy//‌…

user3666197 14.06.2018 21:03

@ user3666197, упс, спасибо за комментарий. Я придерживался python-2.x. Надеюсь, range() работает как с python-3.x, так и с python-2.x. Я исправил ответ. Кстати, внутри joblib.pool есть настоящий модуль multiprocessing, но joblib.pool где-то тормозит (точная блокировка, я думаю), а multiprocessing.Pool - нет.

rth 14.06.2018 21:36

Я очень ценю ваш исправленный код - «многопроцессорность» действительно кажется лучше, чем «joblib» - кто-нибудь знает, почему это правда? Спасибо!

S. Finch 14.06.2018 22:24

@rth после повторного чтения вашего тестирования обнаружена кардинальная ошибка - ваш тест был запущен всего K раз, вместо 400-кратного, поэтому ваши показания времени должны быть увеличены на сотни ... Не могли бы вы любезно повторно запустить тесты и опубликовать соответствующие наблюдения, согласующиеся с определением проблемы O / P?

user3666197 15.06.2018 02:47

@ user3666197 Ой! моя вина! Спасибо, что заметили мою ошибку. Я обновил ответ, но запустил его только для 40 прогулок, а не для 400. Надеюсь, что это нормально. У меня нет времени ждать 12-15 минут. Могу подтвердить, что есть проблема с joblib в версии python-2.x.

rth 15.06.2018 18:50

(Какие свидетельства привели вас к предположению, что проблема связана с joblib как таковым? Возможно, вы начали видеть дорогостоящие затраты на память, связанные с тем, как python-2.x реализовал range() (xrange() не показывает этого, поскольку сам по себе является своего рода итератор [SERIAL], обслуживающий только один раз за цикл), поскольку range() потребует большой блок ОЗУ для выделения памяти для каждого запуска, размещенного и выполняемого на [CONCURRENTLY], что может скоро заблокировать линии связи CPU / RAM вашей платформы, если вы пытаетесь запускать 3+ одновременно, размещенные на вашем локальном хосте класса рабочего стола)

user3666197 15.06.2018 19:27

@ user3666197 Я должен сделать резервную копию с подтверждением. Что ж, это была моя ошибка. Извините (-.-)

rth 15.06.2018 20:41

Другие вопросы по теме