Тупик во вложенном цикле MPI (Python mpi4py) - PullRequest
0 голосов
/ 29 августа 2018

Я не могу понять, почему этот MPI с вложенным циклом не остановится (т.е. тупик). Я знаю, что большинство пользователей MPI основаны на C ++ / C / Fortran, и я здесь использую пакет Python mpi4py, но я подозреваю, что это не проблема языка программирования, а мое неправильное понимание самого MPI.

Код

#!/usr/bin/env python3
# simple_mpi_run.py

from mpi4py import MPI 
import numpy as np 

comm = MPI.COMM_WORLD 
rank = comm.Get_rank() 
size = comm.Get_size() 
root_ = 0 

# Define some tags for MPI  
TAG_BLOCK_IDX = 1

num_big_blocks = 5

for big_block_idx in np.arange(num_big_blocks): 

    for worker_idx in (1+np.arange(size-1)): 
        if rank==root_: 
            # send to workers 
            comm.send(big_block_idx,
                    dest = worker_idx, 
                    tag = TAG_BLOCK_IDX) 
            print("This is big block", big_block_idx, 
                    "and sending to worker rank", worker_idx) 

        else:
            # receive from root_ 
            local_block_idx = comm.recv(source=root_, tag=TAG_BLOCK_IDX) 
            print("This is rank", rank, "on big block", local_block_idx) 

Скрипт пакетного задания

Сценарий пакетного задания SGE, который запускает вышеуказанное. В целях иллюстрации я использую -np 3, чтобы выделить только три процесса для mpirun. В реальном приложении я буду использовать гораздо больше, чем три.

#!/bin/bash

# batch_job.sh

#$ -S /bin/bash 
#$ -pe mpi 3
#$ -cwd
#$ -e error.log
#$ -o stdout.log
#$ -R y

MPIPATH=/usr/lib64/openmpi/bin/

PYTHONPATH=$PYTHONPATH:/usr/local/lib/python3.6/site-packages/:/usr/bin/
export PYTHONPATH

PATH=$PATH:$MPIPATH
export PATH

LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/:/usr/lib64/ 
export LD_LIBRARY_PATH

mpirun -v -np 3 python3 simple_mpi_run.py

выход

Начиная с stdout.log, после запуска qsub batch_job.sh я вижу следующий вывод:

This is big block 0 and sending to worker rank 1
This is rank 1 on big block 0
This is big block 0 and sending to worker rank 2
This is big block 1 and sending to worker rank 1
This is rank 1 on big block 1
This is big block 1 and sending to worker rank 2
This is big block 2 and sending to worker rank 1
This is rank 1 on big block 2
This is big block 2 and sending to worker rank 2
This is big block 3 and sending to worker rank 1
This is rank 1 on big block 3
This is big block 3 and sending to worker rank 2
This is big block 4 and sending to worker rank 1
This is rank 1 on big block 4
This is big block 4 and sending to worker rank 2
This is rank 2 on big block 0
This is rank 2 on big block 1
This is rank 2 on big block 2
This is rank 2 on big block 3
This is rank 2 on big block 4

Задача

Насколько я могу судить, это ожидаемый правильный вывод. Однако, когда я запускаю qstat, я вижу, что состояние задания удерживается на r, что указывает на то, что задание не завершено, даже если у меня есть желаемый вывод. Таким образом, я подозреваю, что это проблема взаимоблокировки MPI, но, несмотря на то, что тут и там часами возятся, я все еще не вижу проблемы с тупиком. Любая помощь приветствуется!


Редактировать

Удалены некоторые блоки комментариев в коде, которые не имеют отношения к рассматриваемой проблеме взаимоблокировки.

1 Ответ

0 голосов
/ 29 августа 2018

Основная причина зависания заключается в том, что вы поменяли местами второй цикл for и предложение if: некорневой ранг должен получить только один раз от мастера.

При этом вы бы предпочли использовать коллектив MPI MPI_Bcast() вместо того, чтобы заново изобретать колесо.

Вот переписанная версия вашей программы

#!/usr/bin/env python3
# simple_mpi_run.py

from mpi4py import MPI 
import numpy as np 

comm = MPI.COMM_WORLD 
rank = comm.Get_rank() 
size = comm.Get_size() 
root_ = 0 

# Define some tags for MPI  
TAG_BLOCK_IDX = 1

num_big_blocks = 5

for big_block_idx in np.arange(num_big_blocks): 

    if rank==root_: 
        for worker_idx in (1+np.arange(size-1)): 
            # send to workers 
            comm.send(big_block_idx,
                    dest = worker_idx, 
                    tag = TAG_BLOCK_IDX) 
            print("This is big block", big_block_idx, 
                    "and sending to worker rank", worker_idx) 

    else:
        # receive from root_ 
        local_block_idx = comm.recv(source=root_, tag=TAG_BLOCK_IDX) 
        print("This is rank", rank, "on big block", local_block_idx) 

и вот более MPI'ish версия, которая использует MPI_Bcast()

#!/usr/bin/env python3
# simple_mpi_run.py

from mpi4py import MPI 
import numpy as np 

comm = MPI.COMM_WORLD 
rank = comm.Get_rank() 
root_ = 0 

num_big_blocks = 5

for big_block_idx in np.arange(num_big_blocks): 

    local_block_idx = comm.bcast(big_block_idx, root=root_)

    if rank==root_: 
            print("This is big block", big_block_idx, 
                    "and broadcasting to all worker ranks")
    else:
        print("This is rank", rank, "on big block", local_block_idx) 
...