Неожиданный результат от MPI isend и irecv

Моей целью было отправить вектор из процесса 0 в процесс 1. Затем отправить его обратно из процесса 1 в процесс 0.

У меня есть два вопроса от моей реализации,

1- Почему отправка обратно из процесса 1 в процесс 0 занимает больше времени, чем наоборот? Первый send-recv занимает всего ~1e-4 секунды, а второй send-recv занимает ~ 1 секунду.

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


mpirun заметил, что процесс с рангом 0 с PID 11248 на узле server1 завершился по сигналу 11 (ошибка сегментации).

Мой обновленный код C++ выглядит следующим образом

#include <mpi.h>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <boost/timer/timer.hpp>
#include <math.h>
using namespace std;
int main(int argc, char** argv) {
    // Initialize the MPI environment
    MPI_Init(NULL, NULL);
    MPI_Request request, request2,request3,request4;

    MPI_Status status;

    int world_size;
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);

    int world_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);

    srand( world_rank );

    int n = 1e3;


    double *myvector = new double[n];
    if (world_rank==0){
        myvector[n-1] = 1;
    }
    MPI_Barrier (MPI_COMM_WORLD);

    if (world_rank==0){

        boost::timer::cpu_timer timer;

        MPI_Isend(myvector, n, MPI_DOUBLE , 1, 0, MPI_COMM_WORLD, &request);

        boost::timer::cpu_times elapsedTime1 = timer.elapsed();
        cout << "  Wallclock time on Process 1:"
                << elapsedTime1.wall / 1e9 << " (sec)" << endl;

        MPI_Irecv(myvector, n, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, &request4);
        MPI_Wait(&request4, &status);

        printf("Test if data is recieved from node 1: %1.0f\n",myvector[n-1]);

        boost::timer::cpu_times elapsedTime2 = timer.elapsed();
        cout <<"  Wallclock time on Process 1:"
                << elapsedTime2.wall / 1e9 << " (sec)" << endl;

    }else{
        boost::timer::cpu_timer timer;

        MPI_Irecv(myvector, n, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &request2);
        MPI_Wait(&request2, &status);

        boost::timer::cpu_times elapsedTime1 = timer.elapsed();
                cout << "  Wallclock time on Process 2:"
                        << elapsedTime1.wall / 1e9 << " (sec)" << endl;

        printf("Test if data is recieved from node 0: %1.0f\n",myvector[n-1]);
        myvector[n-1] = 2;
        MPI_Isend(myvector, n, MPI_DOUBLE , 0, 0, MPI_COMM_WORLD, &request3);
        boost::timer::cpu_times elapsedTime2 = timer.elapsed();
                cout<< "  Wallclock time on Process 2:"
                        << elapsedTime1.wall / 1e9 << " (sec)" << endl;

    }

    MPI_Finalize();

}

Выходные данные: время Wallclock на Процессе 1:2.484e-05 (сек)

Время Wallclock на процессе 2:0,000125325 (сек)

Проверьте, получены ли данные от узла 0: 1

Время Wallclock на процессе 2:0,000125325 (сек)

Проверьте, получены ли данные от узла 1: 2

Время Wallclock на процессе 1:1.00133 (сек)

1 ответ

Сроки расхождения

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

Вы измеряете четыре раза, для двух отправлений, вы только время звонка MPI_Isend, Это Немедленная версия вызова API. Как следует из названия, оно завершается немедленно. Время не имеет ничего общего с фактическим временем отправки сообщения.

Для операций приема вы измеряете MPI_Irecv и соответствующий MPI_Wait, Это время между началом приема и локальной доступностью сообщения. Это снова отличается от времени передачи сообщения, поскольку не учитывает разницу во времени между отправкой получения и соответствующей отправкой. В общем, вы должны учитывать случаи позднего отправителя и позднего получателя. Кроме того, даже для блокировки операций отправки локальное завершение не подразумевает завершенную передачу, удаленное завершение или даже инициацию.

Сроки передачи MPI сложны.

Проверка на завершение

До сих пор остается вопрос, почему что-то в этом коде может занять целую секунду. Это, конечно, не самое подходящее время, если ваша сеть не использует IPoAC. Вероятная причина в том, что вы не проверяете завершение всех сообщений. Реализации MPI часто являются однопоточными и могут прогрессировать в коммуникации только во время соответствующих вызовов API. Чтобы использовать немедленные сообщения, вы должны либо периодически звонить MPI_Test* пока запрос не будет завершен или завершить запрос с помощью MPI_Wait*,

Я не знаю, почему вы решили использовать непосредственные функции MPI. Если вы позвоните MPI_Wait сразу после начала MPI_Isend / MPI_Irecv Вы можете просто позвонить MPI_Send / MPI_Recv, Вам нужны непосредственные функции для одновременного взаимодействия и вычислений, чтобы разрешить одновременные нерегулярные шаблоны связи и избежать взаимоблокировок в определенных ситуациях. Если вам не нужны непосредственные функции, используйте вместо них блокирующие.

Segfault

Хотя я не могу воспроизвести, я подозреваю, что это вызвано использованием того же буфера (myvector) для двух одновременно работающих операций MPI. Не делай этого. Либо используйте отдельный буфер, либо убедитесь, что первая операция завершена. Как правило, вам не разрешается касаться буфера каким-либо образом после его передачи MPI_Isend / MPI_Irecv пока вы не узнаете, что запрос завершен через MPI_Test* / MPI_Wait*,

PS

Если вы считаете, что вам нужны немедленные операции, чтобы избежать взаимоблокировок при отправке и получении, рассмотрите MPI_Sendrecv вместо.

Другие вопросы по тегам