От новичка до гуру: Курсы программирования на CyberDuff

Сообщение MPI получено в другом коммуникаторе - ошибочная программа или ошибка реализации MPI?

Это продолжение этот мой предыдущий вопрос, для которого вывод заключался в том, что программа была ошибочной, и поэтому ожидаемое поведение было неопределенным.

Здесь я пытаюсь создать простой механизм обработки ошибок, для чего я использую запрос Irecv для пустого сообщения в качестве «дескриптора прерывания», присоединяя его к моему обычному вызову MPI_Wait (и превращая его в MPI_WaitAny), чтобы позволить мне разблокировать процесс 1 в случае возникновения ошибки в процессе 0, и он больше не может достичь точки, в которой он должен опубликовать соответствие MPI_Recv.

Происходит то, что из-за внутренней буферизации сообщений MPI_Isend может сразу завершиться успешно, и другой процесс не сможет опубликовать соответствующий MPI_Recv. Так что уже никак не отменить.

Я надеялся, что как только все процессы вызовут MPI_Comm_free, я смогу раз и навсегда забыть об этом сообщении, но, как оказалось, это не так. Вместо этого он доставляется на MPI_Recv в следующем коммуникаторе.

Итак, мои вопросы:

  1. Это тоже ошибочная программа, или это баг в реализации MPI (Intel MPI 4.0.3)?
  2. Если я превращу свои MPI_Isend вызовы в MPI_Issend, программа будет работать как положено - могу ли я хотя бы в этом случае быть уверенным, что программа верна?
  3. Я изобретаю велосипед здесь? Есть ли более простой способ добиться этого?

Опять же, любой отзыв очень ценится!


#include "stdio.h"
#include "unistd.h"
#include "mpi.h"
#include "time.h"
#include "stdlib.h"

int main(int argc, char* argv[]) {
    int rank, size;
    MPI_Group group;
    MPI_Comm my_comm;

    srand(time(NULL));
    MPI_Init(&argc, &argv);

    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_group(MPI_COMM_WORLD, &group);

    MPI_Comm_create(MPI_COMM_WORLD, group, &my_comm);
    if (rank == 0) printf("created communicator %d\n", my_comm);

    if (rank == 1) {
        MPI_Request req[2];
        int msg = 123, which;

        MPI_Isend(&msg, 1, MPI_INT, 0, 0, my_comm, &req[0]);
        MPI_Irecv(NULL, 0, MPI_INT, 0, 0, my_comm, &req[1]);

        MPI_Waitany(2, req, &which, MPI_STATUS_IGNORE);

        MPI_Barrier(my_comm);

        if (which == 0) {
            printf("rank 1: send succeed; cancelling abort handle\n");
            MPI_Cancel(&req[1]);
            MPI_Wait(&req[1], MPI_STATUS_IGNORE);
        } else {
            printf("rank 1: send aborted; cancelling send request\n");
            MPI_Cancel(&req[0]);
            MPI_Wait(&req[0], MPI_STATUS_IGNORE);
        }
    } else {
        MPI_Request req;
        int msg, r = rand() % 2;
        if (r) {
            printf("rank 0: receiving message\n");
            MPI_Recv(&msg, 1, MPI_INT, 1, 0, my_comm, MPI_STATUS_IGNORE);
        } else {
            printf("rank 0: sending abort message\n");
            MPI_Isend(NULL, 0, MPI_INT, 1, 0, my_comm, &req);
        }

        MPI_Barrier(my_comm);

        if (!r) {
            MPI_Cancel(&req);
            MPI_Wait(&req, MPI_STATUS_IGNORE);
        }
    }

    if (rank == 0) printf("freeing communicator %d\n", my_comm);
    MPI_Comm_free(&my_comm);

    sleep(2);

    MPI_Comm_create(MPI_COMM_WORLD, group, &my_comm);
    if (rank == 0) printf("created communicator %d\n", my_comm);

    if (rank == 0) {
        MPI_Request req;
        MPI_Status status;
        int msg, cancelled;

        MPI_Irecv(&msg, 1, MPI_INT, 1, 0, my_comm, &req);
        sleep(1);

        MPI_Cancel(&req);
        MPI_Wait(&req, &status);
        MPI_Test_cancelled(&status, &cancelled);

        if (cancelled) {
            printf("rank 0: receive cancelled\n");
        } else {
            printf("rank 0: OLD MESSAGE RECEIVED!!!\n");
        }
    }

    if (rank == 0) printf("freeing communicator %d\n", my_comm);
    MPI_Comm_free(&my_comm);

    MPI_Finalize();
    return 0;
}

выходы:

created communicator -2080374784
rank 0: sending abort message
rank 1: send succeed; cancelling abort handle
freeing communicator -2080374784
created communicator -2080374784
rank 0: STRAY MESSAGE RECEIVED!!!
freeing communicator -2080374784

  • Стандарт MPI говорит, что когда MPI_Isend отменяется, он должен либо завершиться нормально, либо вообще не быть получен на принимающей стороне. Поскольку ваше сообщение достаточно маленькое, чтобы быть доставленным немедленно, реализация игнорирует MPI_Cancel. Вот почему вы видите, что это не имеет никакого эффекта. 27.05.2014
  • Я предполагаю, что вы имеете в виду последний звонок MPI_Cancel. Было бы совершенно нормально получить это сообщение, если бы оно было в том же коммуникаторе, но, как видите, это другой коммуникатор. 28.05.2014
  • Ваша программа по-прежнему ошибочна, если она имеет непревзойденные сообщения отправки/получения. 28.05.2014
  • возможный дубликат сообщения MPI, полученного в другом коммуникаторе 28.05.2014

Ответы:


1

Как упоминалось в одном из приведенных выше комментариев @kraffenetti, это ошибочная программа, потому что отправленные сообщения не совпадают с полученными. Несмотря на то, что сообщения отменены, они все равно должны иметь соответствующий прием на удаленной стороне, потому что возможно, что отмена не будет успешной для отправленных сообщений из-за того, что они уже были отправлены до того, как отмена может быть завершена (что дело здесь).

Этот вопрос начал обсуждение в билете для MPICH, который вы можете найти здесь там больше подробностей.

28.05.2014
  • Хорошо, спасибо за ответ на первую часть моего вопроса. Что я также хотел бы знать, так это то, останется ли ваше утверждение выше верным, если я заменю MPI_Isend на MPI_Issend? Или программа правильная в этом случае? 29.05.2014
  • Да. Тем не менее, я думаю, что он будет работать в любом случае. 29.05.2014
  • Значит, вы предполагаете, что даже эта очень простая MPI-программа ошибочна и ее ожидаемое поведение не определено? Если да, то я совершенно запутался - в чем тогда цель MPI_Cancel()? Не могли бы вы указать мне соответствующий раздел стандарта MPI или любой другой ресурс, который должен прояснить это для меня? Я был бы очень признателен! 30.05.2014
  • Я не уверен, что могу указать на один. Возможно, вы правы в том, что сделать MPI_ISSEND было бы правильно. Этого, безусловно, было бы достаточно. 31.05.2014

  • 2

    Я пытался собрать ваш код с помощью open mpi, и это не сработало. mpicc пожаловался на status.cancelled

      error: ‘MPI_Status’ has no member named ‘cancelled’
    

    Я предполагаю, что это особенность Intel MPI. Что произойдет, если вы переключитесь на:

        ...
        int flag;
        MPI_Test_cancelled(&status, &flag);
        if (flag) {
        ...
    

    Это дает ожидаемый результат с использованием открытого mpi (и делает ваш код менее зависимым). Это тот случай, когда используется Intel MPI?

    Нам нужен эксперт, чтобы рассказать нам, что такое status.cancelled в Intel MPI, потому что я ничего об этом не знаю!

    Редактировать: я много раз проверял свой ответ и обнаружил, что вывод был случайным, иногда правильным, иногда нет. Извините за это... Как будто что-то в status не было задано. Часть ответа может быть в MPI_Wait(), http://www.mpich.org/static/docs/v3.1/www3/MPI_Wait.html,

    " Поле MPI_ERROR возврата состояния устанавливается только в том случае, если результатом процедуры MPI является MPI_ERR_IN_STATUS. Этот класс ошибки возвращается только процедурами, которые принимают массив аргументов состояния (MPI_Testall, MPI_Testsome, MPI_Waitall и MPI_Waitsome). Во всех В других случаях значение поля MPI_ERROR в статусе не изменяется. Точный текст см. в разделе 3.2.5 спецификации MPI-1.1.

    Итак, вот в чем хитрость: используйте MPI_Waitall(1,&req, &status)! Наконец-то вывод правильный!

    26.05.2014
  • Спасибо за ваше замечание! Я обновил код, чтобы использовать MPI_Test_cancelled(). К сожалению, это не имеет никакого значения - поведение остается прежним. 27.05.2014
  • Я попробовал предложенный вами трюк, но, похоже, он ничего не меняет для меня. 28.05.2014
  • Новые материалы

    Основы Spring: Bean-компоненты, контейнер и внедрение зависимостей
    Как лего может помочь нашему пониманию Когда мы начинаем использовать Spring, нам бросают много терминов, и может быть трудно понять, что они все означают. Итак, мы разберем основы и будем..

    Отслеживание состояния с течением времени с дифференцированием снимков
    Время от времени что-то происходит и революционизирует часть моего рабочего процесса разработки. Что-то более забавное вместо типичного утомительного и утомительного процесса разработки. В..

    Я предполагаю, что вы имеете в виду методы обработки категориальных данных.
    Я предполагаю, что вы имеете в виду методы обработки категориальных данных. Пожалуйста, проверьте мой пост Инструментарий специалиста по данным для кодирования категориальных переменных в..

    Игра в прятки с данными
    Игра в прятки с данными Я хотел бы, чтобы вы сделали мне одолжение и ответили на следующие вопросы. Гуглить можно в любое время, здесь никто не забивается. Сколько регионов в Гане? А как..

    «Раскрытие математических рассуждений с помощью Microsoft MathPrompter и моделей больших языков»
    TL;DR: MathPrompter от Microsoft показывает, как использовать математические рассуждения с большими языковыми моделями; 4-этапный процесс для улучшения доверия и рассуждений в математических..

    Раскройте свой потенциал в области разработки мобильных приложений: Абсолютная бесплатная серия
    Глава 6: Работа в сети и выборка данных Глава 1: Введение в React Native Глава 2: Основы React Native Глава 3: Создание пользовательского интерфейса с помощью React Native Глава 4:..

    Все о кейсах: Camel, Snake, Kebab & Pascal
    В программировании вы сталкивались с ними при именовании переменной, класса или функции. Поддержание согласованности типов и стилей случаев делает ваш код более читабельным и облегчает совместную..