Ошибки передачи данных MPI с mpi4py

У меня есть кластер, и я застрял с ним и mpi4py. У меня довольно сложный код, и MPI просто не может передавать данные. Чтобы прояснить ситуацию, я написал простой код «hello world», который просто передает большие массивы между узлами. Массивы инициализируются 0, а затем заполняются массивами, поступающими из другого узла.

import dill
from mpi4py import MPI
MPI.pickle.__init__(dill.dumps, dill.loads)

comm = MPI.COMM_WORLD
rank = comm.rank

import numpy as np

for k in range(5):
    if rank == 0:
        # node 0 sends hi to other nodes
        for i in range(1, comm.size):
            msg = np.ones(10000000, np.double)
            comm.Send([msg, MPI.DOUBLE], dest=i, tag=0)
    else:
        # other nodes receive hi
        msgin = np.zeros(10000000, np.double)
        comm.Recv([msgin, MPI.DOUBLE], source=0, tag=0)
        with open('solution1.txt', 'a') as f:
            f.write(f'{rank} hi, {msgin[:10]} {np.average(msgin)}\n')
        # and then send reply to 0 node
        msgout = np.ones(10000000)
        comm.Send([msgout, MPI.DOUBLE], dest=0, tag=1)

    if rank == 0:
        # node 0 receives replies
        for i in range(1, comm.size):
            msgin = np.zeros(10000000, np.double)
            comm.Recv([msgin, MPI.DOUBLE], tag=1, source=i)
            with open('solution1.txt', 'a') as f:
                f.write(f'{rank} reply, {msgin[:10]} {np.average(msgin)}\n')


Вот результаты:

1 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
2 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
3 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
4 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
5 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
1 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
2 reply [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
3 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
4 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
5 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
1 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
2 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
3 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
4 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
5 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
1 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
2 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
3 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
4 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
5 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
1 hi [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08

Как видите, иногда вместо 10000000 передается только 6 двойных значений. Этот журнал не полный - все последующие сообщения тоже имеют только 6 значений. Интересно, что результаты воспроизводимы: узел 2 всегда сначала отвечает правильным сообщением, а все остальные узлы отвечают неправильными.

Код отлично работает на одном узле того же кластера. Также он отлично работает в облаке Google (6 узлов по 32 ядра). Я пробовал разные приемы и получил тот же результат:

  • заменил Send / Recv на Isend / Irecv + Wait

  • использовал send / recv со стандартным рассолом и с рассолом из укропа. Этот код не может декодировать данные рассола.

  • пробовал библиотеки openmpi 2.1.1, 4.0.1 и intel mpi

  • попробовал исправить от Intel:

export I_MPI_SHM_LMT=shm

Возможно, возникла проблема с настройкой сети, но я действительно не знаю, что попробовать.

Установка представляет собой многоузловой кластер с межсоединением Mellanox 4x FDR Infiniband в толстом дереве с избыточной подпиской 2–1. Наборы из 24 узлов имеют 12 восходящих каналов к большому коммутатору Infiniband. Каждый узел имеет 64 ГиБ 4-канальной памяти DDR4 SDRAM 2133 МГц (68 ГБ / сек); два процессора Intel Xeon E5-2670 v3 (Haswell).


person Andrew Telyatnik    schedule 17.09.2019    source источник
comment
при ответе попробуйте заменить msgout = np.ones(10000000) на msgout = np.ones(10000000, np.double)   -  person Gilles Gouaillardet    schedule 18.09.2019
comment
Спасибо, я исправил - но numpy использует np.double по умолчанию, поэтому результаты такие же.   -  person Andrew Telyatnik    schedule 18.09.2019


Ответы (2)


В моей настройке я счел полезным всегда использовать один и тот же буфер для получения результатов.

Так что можно попробовать объявить msgin = np.zeros(10000000, np.double) только один раз в начале кода и использовать его снова и снова. Каждый раз при получении сообщения вы можете делать msgin.fill(np.nan), чтобы «очистить» буфер.

Я понятия не имею, почему это сработало для меня, но проблема, похоже, исчезла.

Удачи!

person mdgm    schedule 28.10.2019

У меня была аналогичная проблема в аналогичной кластерной среде. Он появился только в связи с многоузловой связью Infiniband для буферов отправки / приема определенного размера (> 30 МБ). Я мог бы решить проблему, понизив версию OpenMPI 3.x до OpenMPI 2.1.5 (версия Mpi4py кажется неактуальной)

Соответствующий код C работал нормально (тот же кластер, количество ядер / узлов, тот же OpenMPI 3.x и т. Д.), Поэтому проблема, похоже, между mpi4py и OpenMPI 3, но для меня было безнадежно отлаживать.

person alain    schedule 04.03.2020
comment
Я также обнаружил, что при небольшом размере буфера (‹4 МБ в моем случае) проблема исчезла. Еще один обходной путь, который я нашел, - использовать TCP-фабрику для передачи данных. - person Andrew Telyatnik; 05.03.2020
comment
Параметр - I_MPI_FABRICS = shm: tcp. В обоих случаях скорость передачи не была даже близка к спецификациям (около 100 МБ / с против нескольких ГБ / с). Я не мог отлаживать его дальше, это была платформа для соревнований с ограниченным доступом. Конкуренция закончилась, и кластер все равно будет выведен из эксплуатации. - person Andrew Telyatnik; 05.03.2020