Неожиданный результат от 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();

}

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

Время настенных часов в процессе 2:0,000125325 (сек)

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

Время настенных часов в процессе 2:0,000125325 (сек)

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

Время настенных часов в процессе 1:1,00133 (сек)


person C. Panbon    schedule 09.12.2016    source источник
comment
Как вы получили отдельную информацию о времени, если есть только один таймер, и вы его не используете?   -  person Zulan    schedule 09.12.2016
comment
Я сильно подозреваю, что ваш реальный код использует auto_cpu_timer, а не cpu_timer.   -  person Hristo Iliev    schedule 09.12.2016
comment
В любом случае нам нужно увидеть настоящий код, чтобы ответить на первый вопрос.   -  person Zulan    schedule 09.12.2016
comment
Код инициирует неблокирующие операции и никогда не ждет их. Isend в ранге 0 работает, потому что ожидание request4 также продвигает отправку. Но Isend в ранге 1 не прогрессирует, потому что в запросе нет ожидания.   -  person Hristo Iliev    schedule 09.12.2016
comment
@Zulan: я удалил временную часть, чтобы сделать код чище. Я обновил код с помощью таймера.   -  person C. Panbon    schedule 09.12.2016
comment
@HristoIliev, я измерял время на настенных часах. Он инициирует неблокирующую операцию, но ожидает процесса, который получает вектор, используя MPI_Wait.   -  person C. Panbon    schedule 09.12.2016


Ответы (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*.

P.S.

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

person Zulan    schedule 10.12.2016