#python #multithreading #mpi
#python #многопоточность #mpi
Вопрос:
Привет, это довольно конкретный вопрос, поэтому я надеюсь, что StackOverflow предназначен для всех языков программирования, а не только для javascript / html
Я пишу мультипрограмму на MPICH2 (популярный интерфейс передачи сообщений). Моя программа написана на Python, поэтому я использую привязки MPI4Py к Python. MPI лучше всего подходит для ситуаций без разделяемой памяти, поэтому он не идеален для многоядерного программирования. Чтобы использовать полные 4 ядра моего кластера из 5 узлов, я дополнительно использую потоки. Однако я заметил, что использование потоков фактически замедляет мое моделирование. Моя программа состоит из нескольких десятков тысяч строк кода, поэтому я не могу разместить все это, но вот фрагмент, который вызывает проблемы
from threading import Thread
...
threadIndeces=[[0,10],[11,20],[21,30],[31,40]] #subset for each thread
for indeces in treadIndeces:
t=Thread(target=foo,args=(indeces,))
t.start()
Кроме того, я обязательно присоединюсь к потокам позже. Если я запускаю его без потоков и просто вызываю foo
со всеми индексами, это примерно в 10-15 раз быстрее. Когда я записываю время работы многопоточной версии, создание потоков в вызове t=Thread(target=foo,args=(indeces,))
занимает около 0,05 секунды, объединение аналогично занимает 0,05 секунды, но t.start()
вызовы занимают целых 0,2 секунды.
start()
Является дорогостоящим вызовом? Должен ли я менять свой подход? Я думал о сохранении пула потоков, а не о создании новых на каждой итерации, но не похоже, что t=Thread(target=foo,args=(indeces,))
это то, что вызывает замедление.
Кроме того, на случай, если кто-то захочет узнать сложность foo
, вот одна из функций, которая вызывается i
раз за indeces
каждую итерацию (недискретное время):
def HD_training_firing_rate(HD_cell):
"""During training, the firing rate is governed by the difference between the
current heading direction and the preferred heading direction. This is made
to resemble a Gaussian distribution
"""
global fabs
global exp
global direction
#loop over twice due to concurrent CW and CCW HD training
for c in [0,1]:
d=direction[c]
dp=HD_cell.dp #directional preferance
s_d=20.0 #standard deviation
s_i=min(fabs(dp-d),360-fabs(dp-d)) #circular deviation from preferred dir.
HD_cell.r[c]=exp(-s_i*s_i/(2*s_d*s_d)) #normal distribution
Ответ №1:
Если вам нужны потоки, python может быть не лучшим вариантом из-за глобальной блокировки интерпретатора, которая предотвращает истинный параллелизм. Смотрите также тревожный доклад Дейва Бизли.
Возможно, вам было бы лучше просто запустить 20 процессов, чтобы ваши 4 ядра и 5 узлов были заняты, и просто использовать MPI для всех коммуникаций.
Python требует больших накладных расходов на большом железе — возможно, вам захочется подумать о C или C (или, осмелюсь сказать, Fortran?), если вы действительно привержены совместному подходу к передаче потоков и сообщений.
Комментарии:
1. 1 — Я не уверен, в чем преимущество потоков здесь. Похоже, что каждый поток делает что-то полностью независимое? Если да, то в чем преимущество использования потоков по сравнению с MPI? С точки зрения поиска проблем с производительностью, вам будет лучше исключить из уравнения более масштабную декомпозицию и просто сосредоточиться на том, чтобы как можно быстрее запустить что-то в одном блоке общей памяти, затем повторно внедрить межузловую технологию MPI. Если вы застряли, выполняя все это на python, вы вполне можете в конечном итоге обнаружить, что подходы, основанные на процессах, такие как многопроцессорная обработка, превосходят потоки.
2. @Drew Hall: Изначально я использовал C, но этот язык очень утомительный, Python проще в использовании.
3. @Дрю Холл, Джонатан Дурси: Люди, кажется, забывают, что MPI не предполагает разделяемой памяти, тогда как потоки допускают разделяемую память. Проблема здесь в глобальных переменных. Я выполняю большое масштабирование, которое требует вычисления глобального максимума. С помощью потоков этого можно легко достичь, на самом деле это тривиально. Если я использую MPI и запускаю более одного процесса на одном узле, то им придется передавать эти глобальные максимумы. Общение — это, простите за мой язык, заноза в заднице.
4. @puk: Даже с несколькими процессами вы должны иметь возможность открывать сегмент общей памяти для процессов на одном узле. Я не знаю, как это сделать на python, но я был бы удивлен, если это невозможно сделать.
5. @Drew Hall: Я просто хотел указать, почему я решил использовать потоки в дополнение к MPI. Я не уверен на 100%, но я на 99% уверен, что нельзя использовать общую память через MPI.