Как исправить ошибку pickle.unpickling, вызванную вызовами subprocess.Popen в параллельном скрипте, использующем mpi4py

Повторяющиеся последовательные вызовы subprocess.Popen() в сценарии, распараллеленном с mpi4py, в конечном итоге вызывают то, что кажется повреждением данных во время связи, что проявляется в виде ошибки pickle.unpickling различных типов (я видел ошибки распаковки: EOF, недопустимый символ юникода, неверный ключ загрузки, потеря значимости стека распаковки). Кажется, это происходит только тогда, когда передаваемые данные велики, количество последовательных вызовов подпроцесса велико или количество процессов mpi велико.

Я могу воспроизвести ошибку с помощью python>=2.7, mpi4py>=3.0.1 и openmpi>=3.0.0. В конечном итоге я хотел бы общаться с объектами python, поэтому я использую методы mpi4py в нижнем регистре. Вот минимальный код, который воспроизводит ошибку:

#!/usr/bin/env python
from mpi4py import MPI
from copy import deepcopy
import subprocess

nr_calcs           = 4
tasks_per_calc     = 44
data_size          = 55000

# --------------------------------------------------------------------
def run_test(nr_calcs, tasks_per_calc, data_size):

    # Init MPI
    comm = MPI.COMM_WORLD
    rank = comm.Get_rank()
    comm_size = comm.Get_size()                                                                                                                             

    # Run Moc Calcs                                                                                                                                                            
    icalc = 0
    while True:
        if icalc > nr_calcs - 1: break
        index = icalc
        icalc += 1

        # Init Moc Tasks
        task_list = []
        moc_task = data_size*"x"
        if rank==0:
            task_list = [deepcopy(moc_task) for i in range(tasks_per_calc)]
        task_list = comm.bcast(task_list)

        # Moc Run Tasks
        itmp = rank
        while True:
            if itmp > len(task_list)-1: break
            itmp += comm_size
            proc = subprocess.Popen(["echo", "TEST CALL TO SUBPROCESS"],
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False)
            out,err = proc.communicate()

        print("Rank {:3d} Finished Calc {:3d}".format(rank, index))

# --------------------------------------------------------------------
if __name__ == '__main__':
    run_test(nr_calcs, tasks_per_calc, data_size)

Запуск этого на одном 44-ядерном узле с процессами 44 mpi успешно завершает первые 3 «вычисления», но в последнем цикле возникают некоторые процессы:

Traceback (most recent call last):
  File "./run_test.py", line 54, in <module>
    run_test(nr_calcs, tasks_per_calc, data_size)
  File "./run_test.py", line 39, in run_test
    task_list = comm.bcast(task_list)
  File "mpi4py/MPI/Comm.pyx", line 1257, in mpi4py.MPI.Comm.bcast
  File "mpi4py/MPI/msgpickle.pxi", line 639, in mpi4py.MPI.PyMPI_bcast
  File "mpi4py/MPI/msgpickle.pxi", line 111, in mpi4py.MPI.Pickle.load
  File "mpi4py/MPI/msgpickle.pxi", line 101, in mpi4py.MPI.Pickle.cloads
_pickle.UnpicklingError

Иногда UnpicklingError имеет дескриптор, такой как неверный ключ загрузки «x» или ошибка EOF, недопустимый символ Unicode или опустошение стека распаковки.

Редактировать: похоже, проблема исчезает с openmpi‹3.0.0 и с использованием mvapich2, но все же было бы хорошо понять, что происходит.


person Eney    schedule 14.09.2019    source источник
comment
Popen() не блокирует. Он с радостью создаст новые процессы и перейдет к следующей строке выполнения. Возможно, это не ваша проблема, но об этом стоит подумать.   -  person bfris    schedule 17.09.2019
comment
Вызов proc.communicate() будет ждать завершения процесса, но я не думаю, что это основная причина.   -  person Eney    schedule 17.09.2019


Ответы (1)


У меня такая же проблема. В моем случае я заставил свой код работать, установив mpi4py в виртуальной среде Python и установив mpi4py.rc.recv_mprobe = False в соответствии с рекомендациями Intel: https://software.intel.com/en-us/articles/python-mpi4py-on-intel-true-scale-and-omni-path-clusters

Однако в конце концов я просто переключился на использование методов с заглавными буквами Recv и Send с массивами NumPy. Они прекрасно работают с subprocess и не требуют дополнительных ухищрений.

person mdgm    schedule 28.10.2019