使用 MPI_Send 和 MPI_Recv 未正确接收矩阵

Posted

技术标签:

【中文标题】使用 MPI_Send 和 MPI_Recv 未正确接收矩阵【英文标题】:Matrix not received properly with MPI_Send and MPI_Recv 【发布时间】:2021-08-17 17:47:31 【问题描述】:

我是使用 MPI 编程的新手,我有一个练习,我必须使用 MPI_Send 和 MPI_Recv 将两个矩阵相乘,同时将两个矩阵发送到我的进程并将结果发送回根进程。 (两个矩阵都是方阵,N等于进程数)。

这是我写的代码:

#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char *argv[])
srand(time(NULL));

int rank, nproc;
MPI_Status status;

MPI_Init(&argc, &argv);

MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &nproc);

int **matrice = (int **)malloc(nproc * sizeof(int *));
for ( int i=0; i<nproc; i++)
    matrice[i] = (int *)malloc(nproc * sizeof(int));

int **matrice1 = (int **)malloc(nproc * sizeof(int *));
for (int i=0; i<nproc; i++)
    matrice1[i] = (int *)malloc(nproc * sizeof(int));

int **result = (int **)malloc(nproc * sizeof(int *));
for (int i=0; i<nproc; i++)
    result[i] = (int *)malloc(nproc * sizeof(int));

if(rank == 0)
    for(int i = 0; i < nproc; i++)
        for(int j = 0; j < nproc; j++)
            matrice[i][j] = (rand() % 20) + 1;
            matrice1[i][j] = (rand() % 20) + 1;
        
    
    
    for(int i = 1; i < nproc; i++)
        MPI_Send(&(matrice[0][0]), nproc*nproc, MPI_INT, i, 1, MPI_COMM_WORLD);
        MPI_Send(&(matrice1[0][0]), nproc*nproc, MPI_INT, i, 2, MPI_COMM_WORLD);
    
    
else
    MPI_Recv(&(matrice[0][0]), nproc*nproc, MPI_INT, 0, 1, MPI_COMM_WORLD, &status);
    MPI_Recv(&(matrice1[0][0]), nproc*nproc, MPI_INT, 0, 2, MPI_COMM_WORLD, &status);

    
for(int i = 0; i < nproc; i++)
    result[i][j] = 0;
    for(int j = 0; j < nproc; j++)
        result[rank][i] += matrice[rank][j] * matrice1[j][i];       
    


if(rank != 0)
    MPI_Send(&result[rank][0], nproc, MPI_INT, 0, 'p', MPI_COMM_WORLD);



if(rank == 0)
    for(int i = 1; i < nproc; i++)
        MPI_Recv(&result[i][0], nproc, MPI_INT, i, 'p', MPI_COMM_WORLD, &status);
    


MPI_Finalize();


我遇到MPI_SendMPI_Recv 的问题,因为我收到的矩阵只有第一行是正确的,第二行填充了 0,其他都是随机的。

我不明白是什么导致了这个问题。

【问题讨论】:

你应该在连续的内存中分配你的二维矩阵(而不是使用锯齿状数组) 【参考方案1】:

我在使用 MPI_Send 或 MPI_Recv 时遇到问题,因为只有第一个 我收到的矩阵行是正确的,第二行填充 0,其他都是随机的。

您拨打MPI_Send如下:

MPI_Send(&(matrice[0][0]), nproc*nproc, MPI_INT, i, 1, MPI_COMM_WORLD);

所以告诉 MPI 你将从&amp;(matrice[0][0]) 位置开始发送nproc*nproc 元素。 MPI_Send 期望那些 nproc*nproc 元素在内存中连续分配。因此,您的矩阵应该在内存中连续分配。您可以将此类矩阵的内存布局想象为:

| ------------ data used in the MPI_Send -----------|
|     row1          row2         ...      rowN      |
|[0, 1, 2, 3, N][0, 1, 2, 3, N]  ... [0, 1, 2, 3, N]|
\---------------------------------------------------/

从一行的最后一个元素到下一行的第一个元素没有间隙。

不幸的是,您已将矩阵分配为:

int **matrice = (int **)malloc(nproc * sizeof(int *));
for ( int i=0; i<nproc; i++)
    matrice[i] = (int *)malloc(nproc * sizeof(int));

它不会在内存中连续分配一个矩阵,而是分配一个指针数组,这些指针不强制在内存中连续。您可以将该矩阵视为具有以下内存布局:

| ------------ data used in the MPI_Send ----------|
| row1 [0, 1, 2, 3, N] ... (some "random" stuff)   |
\--------------------------------------------------/
  row2 [0, 1, 2, 3, N] ... (some "random" stuff)
  ...
  rowN [0, 1, 2, 3, N] ... (some "random" stuff)

从一行的最后一个元素到下一行的第一个元素可能存在内存间隙。因此,MPI_Send 不可能知道下一行从哪里开始。这就是为什么您可以收到第一行,但不能收到剩余的行。

除其他外,您可以使用以下方法来解决该问题

    在内存中连续分配矩阵; 逐行发送矩阵。

最简单(并且性能更好)的解决方案是您使用第一种方法;查看SO Thread,了解如何为二维数组动态分配连续的内存块。

【讨论】:

以上是关于使用 MPI_Send 和 MPI_Recv 未正确接收矩阵的主要内容,如果未能解决你的问题,请参考以下文章

MPI_Recv() 冻结程序,未从 C 中的 MPI_Send() 接收值

是否需要在对应的 MPI_Recv 之前调用 MPI_Send

mpi_recv 只接收 mpi_send 发送的一半数据?完全糊涂

关于MPI_Send与MPI_Recv语义

MPI_Comm_split 与 MPI_Send / MPI_Recv 的关系

C++ 中的 MPI_Send MPI_Recv 段错误