Вот мой код 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 = 1 CPUTime = 00: 10: 52. Для K = 3 CPUTime = 00: 11: 36. Для K = 5 CPUTime = 00: 12: 58.
@ S.Finch, не могли бы вы также опубликовать фактическое изображение hwloc / lscpu системы NUMA?
@ S.Finch. Не могли бы вы сотрудничать с людьми, которые потратили свое время и знания, чтобы помочь вам? Решение в значительной степени зависит от того, как доступные аппаратные ресурсы используются и планируются в потоке обработки, где планирование чисто-[SERIAL]стандартные инструменты Python могут сделать весь блок из 400-RandomWalks x 1E6-drunken-sailor-steps менее чем в 1.45 [s] вместо ваших опубликованных ~ 11-13 [мин] (!). Если целью было обучать студентов на {R | MATLAB | Julia | Stata} с добавлением подхода Python, опубликуйте карту lstopo тестируемого NUMA-кластера
Боюсь, что кластеры NUMA и Python GlobalInterpreterLock намного превосходят уровень навыков моих студентов (на элементарном семинаре по статистическому программированию). Спасибо за вашу доброту и опыт.
@ S.Finch Приятно слышать, что вы цените опыт и усилия, поддерживая как ваш первоначальный интерес, так и ваши последующие замечания + дополнительные вопросы. Правила сообщества StackOverflow устанавливают, что члены сообщества должны нажимать «Голосовать за» на содержании, которое было интересным, вдохновляющим или имело какое-либо другое положительное значение (щелкните треугольник [^] в верхнем левом углу как в вопросах, так и в ответах, а также в комментариях за такое вознаграждение и указание положительной стоимости) вместо того, чтобы вводить «спасибо» в комментарий. Лучший ответ должен быть отмечен как [Принято]. Так работает StackOverflow. Это честно, не правда ли?






@ 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, конечно, полностью с вами согласен! Накладные расходы и, в случае OP, время распространения шлама имеют значение. Но забавно думать, что, когда мы распределяем процессы, мы ожидаем, что за одно и то же время будет выполнено только умножение работы. Насколько я помню, в библии параллельного программирования есть очень приятное замечание об этом предположении ДАД
Для учебных пособий я успешно распараллелил свой код случайного блуждания, используя специальные модули в R, Matlab, Julia и Stata. (Под «успешно» я имею в виду, что совершенно очевидно, что 20 работников выполняют как минимум в 10 раз больше работы, чем 1 работник за тот же интервал времени.) Возможно ли такое внутреннее распараллеливание нет в Python? Какой модуль предпочтительнее, чем "joblib"? Я бы с удовольствием увидел конкретную ревизию моего кода, которая оказалась успешной. Спасибо!
@rth Для ясности OP явно упомянул питон-3.6, это означает, что ваш код будет генерировать NameError: name 'xrange' is not defined, в то время как python-2.x будет иметь проблемы с памятью после выделения всех блоков памяти экземпляров range():o) >>> tio.run/##K6gsycjPM/7/Py2/SCFTITNPoaIoMS89VUPB0EBB04pLAQgy//…
@ user3666197, упс, спасибо за комментарий. Я придерживался python-2.x. Надеюсь, range() работает как с python-3.x, так и с python-2.x. Я исправил ответ. Кстати, внутри joblib.pool есть настоящий модуль multiprocessing, но joblib.pool где-то тормозит (точная блокировка, я думаю), а multiprocessing.Pool - нет.
Я очень ценю ваш исправленный код - «многопроцессорность» действительно кажется лучше, чем «joblib» - кто-нибудь знает, почему это правда? Спасибо!
@rth после повторного чтения вашего тестирования обнаружена кардинальная ошибка - ваш тест был запущен всего K раз, вместо 400-кратного, поэтому ваши показания времени должны быть увеличены на сотни ... Не могли бы вы любезно повторно запустить тесты и опубликовать соответствующие наблюдения, согласующиеся с определением проблемы O / P?
@ user3666197 Ой! моя вина! Спасибо, что заметили мою ошибку. Я обновил ответ, но запустил его только для 40 прогулок, а не для 400. Надеюсь, что это нормально. У меня нет времени ждать 12-15 минут. Могу подтвердить, что есть проблема с joblib в версии python-2.x.
(Какие свидетельства привели вас к предположению, что проблема связана с joblib как таковым? Возможно, вы начали видеть дорогостоящие затраты на память, связанные с тем, как python-2.x реализовал range() (xrange() не показывает этого, поскольку сам по себе является своего рода итератор [SERIAL], обслуживающий только один раз за цикл), поскольку range() потребует большой блок ОЗУ для выделения памяти для каждого запуска, размещенного и выполняемого на [CONCURRENTLY], что может скоро заблокировать линии связи CPU / RAM вашей платформы, если вы пытаетесь запускать 3+ одновременно, размещенные на вашем локальном хосте класса рабочего стола)
@ user3666197 Я должен сделать резервную копию с подтверждением. Что ж, это была моя ошибка. Извините (-.-)
Не могли бы вы указать время, которое вы получите для различных значений K?