c - 使用 MPI 发送和接收派生数据类型
问题描述
我想以最简单的方式将 NXN 矩阵的反对角元素从根进程发送到另一个进程。可悲的是,目前我无法测试我的代码,因为计算节点已关闭。有人可以检查我的简单代码吗?
我不确定我是否正确发送了 A 的反诊断元素。反对角元素会落在接收缓冲区 B 中吗?
#include "mpi.h"
#include <stdio.h>
int main(int argc, char** argv){
MPI_Init(&argc,&argv);
int size, rank;
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
int N=4;
double A[N][N];
double B[N];
MPI_Datatype antidiag;
int* blockleng=(int*)malloc(N*sizeof(int));
int* displace=(int*)malloc(N*sizeof(int));
for(int i=0; i<N; ++i){
blockleng[i]=1;
displace[i] = (i+1)*(N-1);
}
MPI_Type_indexed(N,blockleng,displace, MPI_DOUBLE,antidiag);
MPI_Type_commit(&antidiag);
MPI_Status status;
if(rank==0){
A= {
{1.0,5.0,9.0,13.0},
{2.0,6.0,10.5,14.5},
{3.0,7.2,11.0,15.0},
{4.0,8.0,12.0,16.0}
};
MPI_Send(A,1,antidiag,1,100,MPI_COMM_WORLD);
}
if(rank==1){
MPI_Recv(B,1,antidiag,0,100,MPI_COMM_WORLD,status);
}
MPI_Type_free(&antidiag);
MPI_Finalize();
return 0;
}
解决方案
您的程序中有几个问题。而不是发布整个程序,我将专注于这些问题。
循环内的变量声明从 C99 开始生效。如果您需要使用旧标准,循环应该是这样的,
int i, j; for(i=0; i<N; ++i){ blockleng[i]=1; displace[i] = (i+1)*(N-1); }
使用最后一个参数创建 MPI 数据类型时
MPI_Type_indexed
,新类型必须作为指针/句柄传递:MPI_Type_indexed(N, blockleng, displace, MPI_DOUBLE, &antidiag);
将值分配给矩阵 A 的方式只能作为初始化与声明相结合的一部分,更多信息请参见此处。当然,您可以分别针对 0 级和其他等级来执行此操作,但您也可以仅从 0 级的文件中读取矩阵数据,
char* file_in = "matrix_A.txt"; FILE *fpi; fpi=fopen(file_in, "r"); for (i=0; i<N; i++) for (j=0; j<N; j++) if (!fscanf(fpi, "%lf", &A[i][j])) break;
作为
if(rank==0){
块的一部分。您还可以使矩阵文件名成为命令行参数,这将为您提供更大的灵活性。此时,您还希望将其N
作为命令行参数来定义您正在读取的矩阵的大小。本例中使用的文件结构简单,1.0 5.0 9.0 13.0 2.0 6.0 10.5 14.5 3.0 7.2 11.0 15.0 4.0 8.0 12.0 16.0
同一块中的发送部分仅发送到单个等级,等级 1。如果我正确理解您的问题,您希望将反对角线从等级 0 发送到所有其他等级,这需要以下循环,
for (i=1; i<size; i++) MPI_Send(A, 1, antidiag, i, 100, MPI_COMM_WORLD);
同样,接收部分应适用于除 0 以外的所有等级,
else{ MPI_Recv(B, N, MPI_DOUBLE, 0, 100, MPI_COMM_WORLD, &status); }
请注意,这里还有其他更正 - 您在 N 大小的缓冲区/数组 B 中接收数据,而不是 NxN 矩阵 A。
atidiag
是为矩阵 A(或 N*N 元素 1D 缓冲区创建的类型,它在这个case) - 它不适用于 N 元素缓冲区 B。因此,您需要更改MPI_Recv
期望/检索 MPI_DOUBLE 类型的 N 个元素。这是一项简洁的功能,因为它允许您将数据接收到与发送数据不同结构的数组中。您还需要将指针传递给status
,因此&status
。
最后,您可以打印结果,
if (rank != 0){
printf("Rank %d:\n", rank);
for (i=0; i<N; i++)
printf("%.2lf ", B[i]);
}
printf("\n\n");
同时请记住,MPI 打印到stout
不是基于排名数字排序的,更重要的是,排名可能在打印过程中相互中断。刷新stdout
可能会有所帮助,但更好的选择是写入文件。在这种情况下,在我的系统上,输出按原样打印。测试多达 6 个进程。
最后一点 - 通常在将 1 个进程发送给所有其他进程的情况下,您应该考虑使用集体通信功能,例如MPI_Bcast
. 这些函数经过高度优化,在专门处理的情况下优于 Send\Recv。现在,Bcast
您需要将接收到的对角线实际存储在所有等级的 A 矩阵中,然后将其复制到其他等级的数组/缓冲区 B比 0。这会增加几个步骤,所以我将其保留在您的帖子中。
推荐阅读
- elasticsearch - Logstash 在所有查询匹配文档中添加新的不存在的嵌套字段?
- python - 如何使用 Python 脚本自动运行外部程序?
- laravel - Laravel UnitTest -> 环境变量被完全忽略
- python - InvalidArgumentError:在 loc 处需要可广播的形状(未知)
- react-native - React Native Flatlist 中的可触摸不透明度关闭
- python - 为什么 len(measure.regionprops(imgl)) 给了我错误的对象数量?
- sql - 在序列sql中查找子序列
- android - Kotlin 中的 SQlite 数据库 android studio
- spring - 本地 ActiveMQ 侦听意外 URL 并拒绝连接
- ios - SwiftUI 预览看起来与模拟器不同