Многопроцессорность Python в mpi
У меня есть python script, который я написал, используя модуль многопроцессорности, для более быстрого выполнения. Расчет смущает параллель, поэтому эффективность масштабируется с количеством процессоров. Теперь я хотел бы использовать это в программе MPI, которая управляет вычислением MCMC на нескольких компьютерах. Этот код имеет вызов system(), который вызывает python script. Тем не менее, я нахожу, что, когда он называется таким образом, выигрыш в эффективности от использования многопроцессорности python исчезает.
Как я могу получить свой python script, чтобы сохранить прирост скорости от многопроцессорности при вызове из MPI?
Вот простой пример, который аналогичен гораздо более сложным кодам, которые я хочу использовать, но демонстрирует одно и то же общее поведение. Я пишу исполняемый python script с именем junk.py.
#!/usr/bin/python
import multiprocessing
import numpy as np
nproc = 3
nlen = 100000
def f(x):
print x
v = np.arange(nlen)
result = 0.
for i, y in enumerate(v):
result += (x+v[i:]).sum()
return result
def foo():
pool = multiprocessing.Pool(processes=nproc)
xlist = range(2,2+nproc)
print xlist
result = pool.map(f, xlist)
print result
if __name__ == '__main__':
foo()
Когда я запускаю это из оболочки сам по себе, используя "верх", я вижу три процесса python, каждый из которых принимает 100% процессора на моей 16-ядерной машине.
node094:mpi[ 206 ] /usr/bin/time junk.py
[2, 3, 4]
2
3
4
[333343333400000.0, 333348333450000.0, 333353333500000.0]
62.68user 0.04system 0:21.11elapsed 297%CPU (0avgtext+0avgdata 16516maxresident)k
0inputs+0outputs (0major+11092minor)pagefaults 0swaps
Однако, если я вызываю это с помощью mpirun, каждый процесс python занимает 33% процессора, и в целом он занимает примерно в три раза больше времени для запуска. Вызов с помощью -np 2 или более приводит к большему количеству процессов, но не ускоряет вычисление.
node094:mpi[ 208 ] /usr/bin/time mpirun -np 1 junk.py
[2, 3, 4]
2
3
4
[333343333400000.0, 333348333450000.0, 333353333500000.0]
61.63user 0.07system 1:01.91elapsed 99%CPU (0avgtext+0avgdata 16520maxresident)k
0inputs+8outputs (0major+13715minor)pagefaults 0swaps
(Дополнительные примечания: Это mpirun 1.8.1, python 2.7.3 в версии для Linux Debian. Я слышал, что система() не всегда разрешена в программах MPI, но она работает для меня последние пять лет этот компьютер. Например, я назвал параллельный код на основе pthread из system() в программе MPI, и он использовал 100% процессора для каждого потока. Также, если вы собираетесь предложить запустить python script в последовательном порядке и просто вызывая его на большее количество узлов... расчет MCMC включает фиксированное количество цепей, которые должны перемещаться синхронно, поэтому, к сожалению, вычисление не может быть реорганизовано таким образом.)
Ответы
Ответ 1
OpenMPI mpirun, v1.7 и более поздние версии, по умолчанию привязывает процессы к ядрам - то есть, когда он запускает процесс punkon junk.py, он привязывает его к ядру, в котором он будет работать. Это хорошо, и правильное поведение по умолчанию для большинства случаев использования MPI. Но здесь каждая задача MPI затем разворачивает больше процессов (через пакет многопроцессорности), и эти разветвленные процессы наследуют состояние привязки своего родителя - поэтому все они связаны с одним и тем же ядром, борясь между собой. (Столбец "P" в верхней части покажет вам, что все они находятся на одном процессоре)
Если вы mpirun -np 2, вы найдете два набора из трех процессов, каждый на другом ядре, каждый из которых соперничает между собой.
С OpenMPI вы можете избежать этого, отключив привязку,
mpirun -np 1 --bind-to none junk.py
или выбирая другое связывание, которое имеет смысл, учитывая конечную геометрию вашего прогона. MPICH имеет аналогичные варианты с гидрой.
Обратите внимание, что fork() ing подпроцессов с mpi не всегда безопасно или поддерживается, особенно с кластерами, запущенными с помощью бесконечных и межсоединений, но OpenMPI mpirun/mpiexec предупредит вас, если он небезопасен.