MPI_Isend не отправляет сообщение немедленно по MPICH v3.3.2 - PullRequest
1 голос
/ 26 февраля 2020

Я пишу программу для параллельного вычисления умножения матриц и использую функцию MPI_Isend для неблокирующей связи. Вот основные логики распараллеливания c ::

  • Главный узел отправляет большую часть работы разным работникам.
  • Затем он выполняет часть самой работы. Он ожидает, что рабочие - тем временем - начали получать свою работу и работать на ней.
  • Он ждет, когда рабочие завершат свою работу, позвонив по номеру MPI_Wait, а затем MPI_Recv.

Но на самом деле рабочие получают свои сообщения только тогда, когда главный узел вызывает MPI_Wait; поэтому, когда главный узел выполняет свою долю работы, другие узлы не работают. Почему это происходит?

#include "helpers.h"
#include <mpi.h>


int main(int argc, char const *argv[]) {
    int m = atoi(argv[1]);
    int n = atoi(argv[2]);
    int p = atoi(argv[3]);
    double start, elapsed;

    MPI_Init(NULL, NULL);
    int world_size, world_rank, name_len;
    char processor_name[MPI_MAX_PROCESSOR_NAME];
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
    MPI_Get_processor_name(processor_name, &name_len);
    printf("Hello world from processor %s, rank %d out of %d processors\n", processor_name, world_rank, world_size);

    float *A = malloc(m * n * sizeof(float));
    float *B = malloc(n * p * sizeof(float));
    float *C = malloc(m * p * sizeof(float));
    float *C_serial = malloc(m * p * sizeof(float));

    if (world_rank == 0) {
        randarr(m*n, A);
    } else if (world_rank == 1) {
        randarr(n*p, B);
    }
    MPI_Barrier(MPI_COMM_WORLD);
    start = MPI_Wtime();

    // Serial multiplication
    if (!world_rank) {
        Multiply_serial(A, B, C_serial, m, n, p);
        elapsed = MPI_Wtime() - start;
        printf("[*] Serial multiplication: %f seconds\n", elapsed);
    }

    // Parallel multiplication
    int mpart = m / world_size;
    MPI_Request request_ids[(world_size-1)*2];
    float *Apart = malloc(mpart * n * sizeof(float));
    float *Cpart = malloc(mpart * p * sizeof(float));

    // Master node
    if (!world_rank) {
        for (int i = 1; i < world_size; ++i) {
            MPI_Isend(&A[i*mpart*n], mpart*n, MPI_FLOAT, i, 1, MPI_COMM_WORLD, &request_ids[(i-1)*2]);
            MPI_Isend(B, n*p, MPI_FLOAT, i, 2, MPI_COMM_WORLD, &request_ids[(i-1)*2 + 1]);
        }
        printf("[*] Started sending: %f seconds\n", MPI_Wtime() - start);
        // Master node's share of multiplication
        for (int i = 0; i < mpart; ++i) {
            for (int j = 0; j < p; ++j) {
                C[i*p + j] = 0.0;
                for (int k = 0; k < n; ++k) {
                    C[i*p + j] += + A[i*n + k] * B[k*p + j];
                }
            }
        }
        printf("[*] Multiplied: %f seconds\n", MPI_Wtime() - start);
        MPI_Waitall((world_size-1)*2, request_ids, MPI_STATUSES_IGNORE);
        printf("[*] Sending completed: %f seconds\n", MPI_Wtime() - start);
        for (int i = 1; i < world_size; ++i) {
            MPI_Recv(&C[i*mpart*p], mpart*p, MPI_FLOAT, i, 3, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        }

        elapsed = MPI_Wtime() - start;
        printf("[*] Parallel multiplication: %f seconds\n", elapsed);

        int correct = IsEqual(C, C_serial, m, p);
        printf("[*] Parallel correctness: %d\n", correct);
    }

    // Worker nodes 
    if (world_rank) {
        MPI_Recv(Apart, mpart*n, MPI_FLOAT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        MPI_Recv(B, n*p, MPI_FLOAT, 0, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        printf("[*] I'm worker, got work: %f seconds\n", MPI_Wtime() - start);

        for (int i = 0; i < mpart; ++i) {
            for (int j = 0; j < p; ++j) {
                Cpart[i*p + j] = 0.0;
                for (int k = 0; k < n; ++k) {
                    Cpart[i*p + j] += + Apart[i*n + k] * B[k*p + j];
                }
            }
        }
        MPI_Send(Cpart, mpart*p, MPI_FLOAT, 0, 3, MPI_COMM_WORLD);
    }

    MPI_Finalize();
    return 0;
}

А вот пример запуска программы: *

sumit@HAL9000:~/Coding/parallel/parallel-distributed-assignments/A2$ time make mpinb
mpicc mpib.c -o mpib
mpicc mpinb.c -o mpinb
mpirun -n 4 ./mpinb 6000 32 6000
Hello world from processor HAL9000, rank 0 out of 4 processors
Hello world from processor HAL9000, rank 1 out of 4 processors
Hello world from processor HAL9000, rank 2 out of 4 processors
Hello world from processor HAL9000, rank 3 out of 4 processors
[*] Serial multiplication: 4.700643 seconds
[*] Started sending: 4.700674 seconds
[*] Multiplied: 5.790390 seconds
[*] I'm worker, got work: 5.790726 seconds
[*] I'm worker, got work: 5.790901 seconds
[*] Sending completed: 5.790974 seconds
[*] I'm worker, got work: 5.791019 seconds
[*] Parallel multiplication: 6.920345 seconds
[*] Parallel correctness: 1

real    0m7.175s
user    0m27.699s
sys 0m0.479s

1 Ответ

1 голос
/ 27 февраля 2020

Вы запускали его несколько раз, и он вел себя одинаково?

Попытайтесь сказать про c "rank = 0", чтобы он сделал значительную паузу (возможно, 10 секунд?) Перед паузой MPI_Waitall вызов. Потому что я не уверен, что это работает не так, как вы хотите. Я думаю, что это может быть просто потому, что процесс связи длиннее, чем выполнение трех циклов for, поэтому работники заканчивают свою работу после про c "rank = 0".

PS: Я также думаю, что 2 ваших сообщения printf не совпадают в вашем коде и в exe c («Только что отправлено» и «Отправка завершена», похоже, «Началась отправка» и «Отправка завершена» вашего кода?)

...