mpi4py с процессами и потоками

#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.