您的位置:首页 > 其它

MPI学习二 MPI并行程序的两种基本模式

2017-02-27 19:13 405 查看
MPI的两种最基本的并行程序设计模式 即对等模式和主从模式。

  对等模式:各个部分地位相同,功能和代码基本一致,只不过是处理的数据或对象不同,也容易用同样的程序来实现。

  主从模式:分为主进程和从进程,程序通信进程之间的一种主从或依赖关系 。MPI程序包括两套代码,主进程运行其中一套代码,从进程运行另一套代码。


1. 对等模式MPI程序设计


1.1 Jacobi 迭代

  一句话概括就是矩阵每个元素都是其上下左右四个元素的平均值,矩阵四个边界的值不变。


1.2 用MPI程序实现Jacobi 迭代

  将参加迭代的数据按列进行分割 并假设一共有4个进程同时并行计算。

  假设需要迭代的数据是 M*M 的二维数组 A(M-1,M-1),令 M=4*N,按图示进行数据划分,则分布在四个不同进程上的数据分别是,

进程0:A(M-1, 0 : N-1)

进程1:A(M-1, N : 2*N-1)

进程2:A(M-1, 2*N : 3*N-1)

进程3:A(M-1, 3*N : M-1)

  由于在迭代过程中,边界点新值的计算需要相邻边界其它块的数据,因此在每一个数据块的两侧又各增加1列的数据空间,用于存放从相邻数据块通信得到的数据。这样原来每个数据块的大小从 M*N 扩大到 M*N+2,进程0和进程1的数据块只需扩大一块即可满足通信的要求, 但这里为了编程的方便和形式的一致,在两边都增加了数据块。

  计算和通信过程:

   1. 对数组赋初值,边界赋为8,内部赋为0。

   2. 每个进程都需要从相邻的进程得到数据块,同时每一个进程也都需要向相邻的进程提供数据块。注意:通信次序保证进程之间不会出现死锁。

   3. 进行Jacobi迭代。

#include "mpi.h"

#include <stdio.h>


#define totalsize 16

#define mysize totalsize/4

#define steps 10


void main(int argc,char* argv[])

{

int myid,numprocs,n,i,j,rc;

float a[totalsize][mysize+2];

float b[totalsize][mysize+2]; //临时数组用来记录临时得到的新值

float temp[totalsize]; //进程间通信得到的数据

int begin_col,end_col,ierr;

MPI_Status status;


MPI_Init(&argc,&argv);


MPI_Comm_rank(MPI_COMM_WORLD,&myid);

MPI_Comm_size(MPI_COMM_WORLD,&numprocs);

fprintf(stderr,"Process %d of %d is alive.\n",myid,numprocs);


//初始化

for(j=0;j<mysize+2;j++)

for(i=0;i<totalsize;i++)

a[i][j]=0.0;

if(myid==0)

for(i=0;i<totalsize;i++)

a[i][1]=8.0;

if(myid==3)

for(i=0;i<totalsize;i++)

a[i][mysize]=8.0;

for(i=1;i<mysize+1;i++)

{

a[0][i]=8.0;

a[totalsize-1][i]=8.0;

}


//Jacobi 迭代

for(n=1;n<=steps;n++)

{

//从右侧邻居得到数据

if(myid<3)

{

MPI_Recv(&temp[0],totalsize,MPI_FLOAT,myid+1,10,MPI_COMM_WORLD,&status);

for(i=0;i<totalsize;i++)

a[i][mysize+1]=temp[i];

}

//向左侧邻居发送数据

if(myid>0)

{

for(i=0;i<totalsize;i++)

temp[i]=a[i][1];

MPI_Send(&temp[0],totalsize,MPI_FLOAT,myid-1,10,MPI_COMM_WORLD);

}

//向右侧邻居发送数据

if(myid<3)

{

for(i=0;i<totalsize;i++)

temp[i]=a[i][mysize];

MPI_Send(&temp[0],totalsize,MPI_FLOAT,myid+1,10,MPI_COMM_WORLD);

}

//从左侧邻居得到数据

if(myid>0)

{

MPI_Recv(&temp[0],totalsize,MPI_FLOAT,myid-1,10,MPI_COMM_WORLD,&status);

for(i=0;i<totalsize;i++)

a[i][0]=temp[i];

}


begin_col=1;

end_col=mysize;

if(myid==0)

begin_col=2;

if(myid==3)

end_col=mysize-1;


for(j=begin_col;j<=end_col;j++)

for(i=1;i<totalsize-1;i++)

b[i][j]=0.25*(a[i][j+1]+a[i][j-1]+a[i+1][j]+a[i-1][j]);

for(j=begin_col;j<=end_col;j++)

for(i=1;i<totalsize-1;i++)

a[i][j]=b[i][j]; //一次迭代完成后,统一进行更新


}


MPI_Barrier(MPI_COMM_WORLD);


fprintf(stderr,"\nProcess %d :\n",myid);

begin_col=1;

end_col=mysize;

for(i=0;i<totalsize;i++)

{

for(j=begin_col;j<=end_col;j++)

fprintf(stderr,"%.2fP%d\t",a[i][j],myid);

fprintf(stderr,"\n");

}

fprintf(stderr,"\n");


MPI_Finalize();

}

[/code]


运行结果

[root@node1 mpi]# mpirun -np 4 ./test-8-1-2

Process 1 of 4 is alive.
Process 2 of 4 is alive.
Process 3 of 4 is alive.
Process 0 of 4 is alive.

Process 0 :
8.00P0
Process 1 :
8.00P1  8.00P1  8.00P1  8.00P1
5.54P1  5.39P1  5.33P1  5.32P1
3.48P1  3.21P1  3.11P1  3.08P1
2.05P1  1.70P1
Process 2 :
8.00P2  8.00P2  8.00P2  8.00P2
5.32P2  5.33P2  5.39P2  5.54P2
3.08P2  3.11P2  3.21P2  3.48P2
1.53P2  1.57P2  1.70P2  2.05P2
0.64P2  0.68P2
Process 3 :
8.00P3  8.00P3  8.00P3  8.00P3
5.84P3  6.37P3  7.11P3  8.00P3
4.05P3  5.00P3  6.37P3  8.00P3
2.78P3  4.05P3  5.84P3  8.00P3
2.05P3  3.48P3  5.54P3  8.00P3
1.70P3  3.21P3  5.39P3  8.00P0  8.00P0  8.00P0
8.00P0  7.11P0  6.37P0  5.84P0
8.00P0  6.37P0  5.00P0  4.05P0
8.00P0  5.84P0  4.05P0  2.78P0
8.00P0  5.54P0  3.48P0  2.05P0
8.00P0  5.39P0  3.21P0  1.70P0
8.00P0  5.33P0  3.11P0  1.57P0
8.00P0  1.57P1  1.53P1
1.22P1  0.83P1  0.68P1  0.64P1
0.83P1  0.42P1  0.27P1  0.23P1
0.68P1  0.27P1  0.12P1  0.07P1
0.64P1  0.23P1  0.07P1  0.03P1
0.64P1  0.23P1  0.83P2  1.22P2
0.23P2  0.27P2  0.42P2  0.83P2
0.07P2  0.12P2  0.27P2  0.68P2
0.03P2  0.07P2  0.23P2  0.64P2
0.03P2  0.07P2  0.23P2  8.00P3
1.57P3  3.11P3  5.33P3  8.00P3
1.53P3  3.08P3  5.32P3  8.00P3
1.53P3  3.08P3  5.32P3  8.00P3
1.57P3  3.11P3  5.33P3  8.00P3
1.70P3  5.32P0  3.08P0  1.53P0
8.00P0  5.32P0  3.08P0  1.53P0
8.00P0  5.33P0  3.11P0  1.57P0
8.00P0  5.39P0  3.21P0  1.70P0
0.07P1  0.03P1
0.68P1  0.27P1  0.12P1  0.07P1
0.83P1  0.42P1  0.27P1  0.23P1
1.22P1  0.83P1  0.68P1  0.64P1  0.64P2
0.07P2  0.12P2  0.27P2  0.68P2
0.23P2  0.27P2  0.42P2  0.83P2
0.64P2  0.68P2  0.83P2  1.22P2
1.53P2  3.21P3  5.39P3  8.00P3
2.05P3  3.48P3  5.54P3  8.00P3
2.78P3  4.05P3  5.84P3  8.00P3
4.05P3  5.00P3  6.37P3  8.00P3
8.00P0  5.54P0  3.48P0  2.05P0
8.00P0  5.84P0  4.05P0  2.78P0
8.00P0  6.37P0  5.00P0  4.05P0
8.00P0
2.05P1  1.70P1  1.57P1  1.53P1
3.48P1  3.21P1  3.11P1  3.08P1
5.54P1  5.39P1  5.33P1  5.32P1
8.00P1  1.57P2  1.70P2  2.05P2
3.08P2  3.11P2  3.21P2  3.48P2
5.32P2  5.33P2  5.39P2  5.54P2
8.00P2  8.00P2  5.84P3  6.37P3  7.11P3  8.00P3
8.00P3  8.00P3  8.00P3  8.00P3

7.11P0  6.37P0  5.84P0
8.00P0  8.00P0  8.00P0  8.00P0

8.00P1  8.00P1  8.00P1

8.00P2  8.00P2

整理后

Process 0 :
8.00P0  8.00P0  8.00P0  8.00P0
8.00P0  7.11P0  6.37P0  5.84P0
8.00P0  6.37P0  5.00P0  4.05P0
8.00P0  5.84P0  4.05P0  2.78P0
8.00P0  5.54P0  3.48P0  2.05P0
8.00P0  5.39P0  3.21P0  1.70P0
8.00P0  5.33P0  3.11P0  1.57P0
8.00P0  5.32P0  3.08P0  1.53P0
8.00P0  5.32P0  3.08P0  1.53P0
8.00P0  5.33P0  3.11P0  1.57P0
8.00P0  5.39P0  3.21P0  1.70P0
8.00P0  5.54P0  3.48P0  2.05P0
8.00P0  5.84P0  4.05P0  2.78P0
8.00P0  6.37P0  5.00P0  4.05P0
8.00P0  7.11P0  6.37P0  5.84P0
8.00P0  8.00P0  8.00P0  8.00P0

Process 1 :
8.00P1  8.00P1  8.00P1  8.00P1
5.54P1  5.39P1  5.33P1  5.32P1
3.48P1  3.21P1  3.11P1  3.08P1
2.05P1  1.70P1  1.57P1  1.53P1
1.22P1  0.83P1  0.68P1  0.64P1
0.83P1  0.42P1  0.27P1  0.23P1
0.68P1  0.27P1  0.12P1  0.07P1
0.64P1  0.23P1  0.07P1  0.03P1
0.64P1  0.23P1  0.07P1  0.03P1
0.68P1  0.27P1  0.12P1  0.07P1
0.83P1  0.42P1  0.27P1  0.23P1
1.22P1  0.83P1  0.68P1  0.64P1
2.05P1  1.70P1  1.57P1  1.53P1
3.48P1  3.21P1  3.11P1  3.08P1
5.54P1  5.39P1  5.33P1  5.32P1
8.00P1  8.00P1  8.00P1  8.00P1

Process 2 :
8.00P2  8.00P2  8.00P2  8.00P2
5.32P2  5.33P2  5.39P2  5.54P2
3.08P2  3.11P2  3.21P2  3.48P2
1.53P2  1.57P2  1.70P2  2.05P2
0.64P2  0.68P2  0.83P2  1.22P2
0.23P2  0.27P2  0.42P2  0.83P2
0.07P2  0.12P2  0.27P2  0.68P2
0.03P2  0.07P2  0.23P2  0.64P2
0.03P2  0.07P2  0.23P2  0.64P2
0.07P2  0.12P2  0.27P2  0.68P2
0.23P2  0.27P2  0.42P2  0.83P2
0.64P2  0.68P2  0.83P2  1.22P2
1.53P2  1.57P2  1.70P2  2.05P2
3.08P2  3.11P2  3.21P2  3.48P2
5.32P2  5.33P2  5.39P2  5.54P2
8.00P2  8.00P2  8.00P2  8.00P2

Process 3 :
8.00P3  8.00P3  8.00P3  8.00P3
5.84P3  6.37P3  7.11P3  8.00P3
4.05P3  5.00P3  6.37P3  8.00P3
2.78P3  4.05P3  5.84P3  8.00P3
2.05P3  3.48P3  5.54P3  8.00P3
1.70P3  3.21P3  5.39P3  8.00P3
1.57P3  3.11P3  5.33P3  8.00P3
1.53P3  3.08P3  5.32P3  8.00P3
1.53P3  3.08P3  5.32P3  8.00P3
1.57P3  3.11P3  5.33P3  8.00P3
1.70P3  3.21P3  5.39P3  8.00P3
2.05P3  3.48P3  5.54P3  8.00P3
2.78P3  4.05P3  5.84P3  8.00P3
4.05P3  5.00P3  6.37P3  8.00P3
5.84P3  6.37P3  7.11P3  8.00P3
8.00P3  8.00P3  8.00P3  8.00P3



1.3 用捆绑发送接收实现Jacobi 迭代

MPI提供了捆绑发送和接收操作,可以在一条MPI语句中同时实现向其它进程的数据发送和从其它进程接收数据操作。可以有效地避免不合理的通信次序,最大限度避免死锁的产生。

int MPI_Sendrecv(void *sendbuf, int sendcount,MPI_Datatype sendtype, int dest, int sendtag, void *recvbuf, int recvcount, MPI_Datatype recvtype, int source, int recvtag, MPI_Comm comm, MPI_Status
*status)

对于Jacobi迭代 发送和接收操作是成对出现的,因此特别适合使用捆绑发送接收操作调。对于中间块来说,每一块都要向两侧的相邻块发送数据,同时也要从两侧的相邻块接收数据,可以非常方便地用MPI_SENDRECV调用来实现。但是对于左侧和右侧的边界块,却不容易将各自的发送和接收操作合并到一个调用之中,因此程序中对边界块仍编写单独的发送和接收语句。

#include "mpi.h"

#include <stdio.h>


#define totalsize 16

#define mysize totalsize/4

#define steps 10


void main(int argc,char* argv[])

{

int myid,numprocs,n,i,j,rc;

float a[totalsize][mysize+2],b[totalsize][mysize+2];

float temp[totalsize],temp2[totalsize];

int begin_col,end_col,ierr;

MPI_Status status;


MPI_Init(&argc,&argv);


MPI_Comm_rank(MPI_COMM_WORLD,&myid);

MPI_Comm_size(MPI_COMM_WORLD,&numprocs);

fprintf(stderr,"Process %d of %d is alive.\n",myid,numprocs);


//初始化

for(j=0;j<mysize+2;j++)

for(i=0;i<totalsize;i++)

a[i][j]=0.0;

if(myid==0)

for(i=0;i<totalsize;i++)

a[i][1]=8.0;

if(myid==3)

for(i=0;i<totalsize;i++)

a[i][mysize]=8.0;

for(i=1;i<mysize+1;i++)

{

a[0][i]=8.0;

a[totalsize-1][i]=8.0;

}


//Jacobi 迭代

for(n=1;n<=steps;n++)

{

//从左向右发送与接收数据

if(myid==0) 

{

for(i=0;i<totalsize;i++)

temp[i] = a[i][mysize];

MPI_Send(&temp[0],totalsize,MPI_FLOAT,1,10,MPI_COMM_WORLD);

}

else if(myid==3)

{

MPI_Recv(&temp[0],totalsize,MPI_FLOAT,2,10,MPI_COMM_WORLD,&status);

for(i=0;i<totalsize;i++)

a[i][0] = temp[i];

}

else

{

for(i=0;i<totalsize;i++)

temp[i] = a[i][mysize];

MPI_Sendrecv(&temp[0],totalsize,MPI_FLOAT,myid+1,10,&temp2[0],totalsize,MPI_FLOAT,myid-1,10,MPI_COMM_WORLD,&status);

for(i=0;i<totalsize;i++)

a[i][0] = temp2[i];

}

//从右向左发送与接收数据

if(myid==0)

{

MPI_Recv(&temp[0],totalsize,MPI_FLOAT,1,10,MPI_COMM_WORLD,&status);

for(i=0;i<totalsize;i++)

a[i][mysize+1] = temp[i];

}

else if(myid==3)

{

for(i=0;i<totalsize;i++)

temp[i] = a[i][1];

MPI_Send(&temp[0],totalsize,MPI_FLOAT,2,10,MPI_COMM_WORLD);

}

else

{

for(i=0;i<totalsize;i++)

temp[i] = a[i][1];

MPI_Sendrecv(&temp[0],totalsize,MPI_FLOAT,myid-1,10,&temp2[0],totalsize,MPI_FLOAT,myid+1,10,MPI_COMM_WORLD,&status);

 for(i=0;i<totalsize;i++)

a[i][mysize+1] = temp2[i];

}


begin_col=1;

end_col=mysize;

if(myid==0)

begin_col=2;

if(myid==3)

end_col=mysize-1;


for(j=begin_col;j<=end_col;j++)

for(i=1;i<totalsize-1;i++)

b[i][j]=0.25*(a[i][j+1]+a[i][j-1]+a[i+1][j]+a[i-1][j]);

for(j=begin_col;j<=end_col;j++)

for(i=1;i<totalsize-1;i++)

a[i][j]=b[i][j];


}


MPI_Barrier(MPI_COMM_WORLD);


fprintf(stderr,"\nProcess %d :\n",myid);

begin_col=1;

end_col=mysize;

for(i=0;i<totalsize;i++)

{

for(j=begin_col;j<=end_col;j++)

fprintf(stderr,"%.2fP%d\t",a[i][j],myid);


fprintf(stderr,"\n");

}

fprintf(stderr,"\n");


MPI_Finalize();

}

[/code]


1.4 引入虚拟进程后Jacobi 迭代的实现

  虚拟进程(MPI_PROC_NULL) 是不存在的假想进程,在MPI中的主要作用是充当真实进程通信的目或源,引入虚拟进程的目的是为了在某些情况下编写通信语句的方便。当一个真实进程向一个虚拟进程发送数据或从一个虚拟进程接收数据时,该真实进程会立即正确返回,如同执行了一个空操作。

  在捆绑发送接收操作中经常用到这种通信手段,一个真实进程向虚拟进程发送消息时会立即成功返回;一个真实进程从虚拟进程的接收消息时也会立即成功返回,并且对接收缓冲区没有任何改变。

#include "mpi.h"

#include <stdio.h>


#define totalsize 16

#define mysize totalsize/4

#define steps 10


void main(int argc,char* argv[])

{

int myid,numprocs,n,i,j,rc;

float a[totalsize][mysize+2],b[totalsize][mysize+2];

float temp[totalsize],temp2[totalsize];

int begin_col,end_col,ierr;

MPI_Status status;


int left,right,tag1,tag2;


MPI_Init(&argc,&argv);


MPI_Comm_rank(MPI_COMM_WORLD,&myid);

MPI_Comm_size(MPI_COMM_WORLD,&numprocs);

fprintf(stderr,"Process %d of %d is alive.\n",myid,numprocs);

//初始化

for(j=0;j<mysize+2;j++)

for(i=0;i<totalsize;i++)

a[i][j]=0.0;

if(myid==0)

for(i=0;i<totalsize;i++)

a[i][1]=8.0;

if(myid==3)

for(i=0;i<totalsize;i++)

a[i][mysize]=8.0;

for(i=1;i<mysize+1;i++)

{

a[0][i]=8.0;

a[totalsize-1][i]=8.0;

}


tag1=3,tag2=4;

//设置当前进程的左右两侧进程标识

if(myid>0)

left=myid-1;

else

left=MPI_PROC_NULL;

if(myid<3)

right=myid+1;

else

right=MPI_PROC_NULL;


//Jacobi迭代

for(n=1;n<=steps;n++)

{

    //从左向右发送与接收数据

for(i=0;i<totalsize;i++)

    temp[i]=a[i][mysize];

MPI_Sendrecv(&temp[0],totalsize,MPI_FLOAT,right,tag1,&temp2[0],totalsize,MPI_FLOAT,left,tag1,MPI_COMM_WORLD,&status);

for(i=0;i<totalsize;i++)

a[i][0]=temp2[i];


    //从右向左发送与接收数据

for(i=0;i<totalsize;i++)

    temp[i]=a[i][1];

MPI_Sendrecv(&temp[0],totalsize,MPI_FLOAT,left,tag2,&temp2[0],totalsize,MPI_FLOAT,right,tag2,MPI_COMM_WORLD,&status);

for(i=0;i<totalsize;i++)

a[i][mysize+1]=temp2[i];


begin_col=1;

end_col=mysize;

if(myid==0)

        begin_col=2;

if(myid==3)

        end_col=mysize-1;


    for(j=begin_col;j<=end_col;j++)

        for(i=1;i<totalsize-1;i++)

            b[i][j]=0.25*(a[i][j+1]+a[i][j-1]+a[i+1][j]+a[i-1][j]);

    for(j=begin_col;j<=end_col;j++)

        for(i=1;i<totalsize-1;i++)

            a[i][j]=b[i][j];

}

MPI_Barrier(MPI_COMM_WORLD);


fprintf(stderr,"\nProcess %d :\n",myid);

begin_col=1;

end_col=mysize;

for(i=0;i<totalsize;i++)

{

    for(j=begin_col;j<=end_col;j++)

        fprintf(stderr,"%.2fP%d\t",a[i][j],myid);

fprintf(stderr,"\n");

}

fprintf(stderr,"\n");


MPI_Finalize();

}

[/code]


2 主从模式的MPI程序设计


2.1 矩阵向量乘 矩阵A×向量B=向量C

实现过程:

   1. 主进程将向量B广播给所有的从进程

   2. 主进程循环向各个从进程发送矩阵A的一行的数据

   3. 从进程计算一行和B相乘的结果,并将结果发送给主进程。

   4. 一旦主进程将A的各行发送完毕,则每收到一个结果,就向相应的从进程发送结束标志,从进程接收到结束标志后退出执行。

   5. 主进程收集完所有的结果后也结束。

#include "mpi.h"

#include <stdio.h>

#include <stdlib.h>

#include <time.h>


#define MAX_ROWS 100

#define MAX_COLS 100

#define ExitTag 999

#define min(x,y) ((x)>(y)?(y):(x))

void main(int argc,char* argv[])

{

int myid,numprocs,master;

int i,j,numsend,numrecv,sender;

int rows,cols;

double A[MAX_ROWS][MAX_COLS],b[MAX_ROWS],c[MAX_ROWS];

double buf[MAX_ROWS],result;

int result_Tag,result_row,n;

MPI_Status status;


MPI_Init(&argc,&argv);


MPI_Comm_rank(MPI_COMM_WORLD,&myid);

MPI_Comm_size(MPI_COMM_WORLD,&numprocs);

fprintf(stderr,"Process %d in %d \n",myid,numprocs);


master = 0;

rows = 10;

cols = 10;


if(myid == master)

{

   //初始化

for(i=0;i<rows;i++)

for(j=0;j<cols;j++)

A[i][j] = i;

for(j=0;j<rows;j++)

b[j] = 1;


numsend = 0; //记录当前发送的行数

//将向量b广播至从进程

MPI_Bcast(b,rows,MPI_DOUBLE,master,MPI_COMM_WORLD);

//确定从进程数,取行数和numprocs-1中小的为从进程数

n = min(numprocs-1,rows);

//将矩阵A按行划分,发送给从进程

for(i=0;i<n;i++)

{

for(j=0;j<cols;j++)

buf[j] = A[i][j];

//发送数据,并标记当前行

MPI_Send(buf,cols,MPI_DOUBLE,i+1,i,MPI_COMM_WORLD);

numsend++;

}

//对向量c的每一行接收来自从进程的计算结果

for(i=0;i<rows;i++)

{

MPI_Recv(&result,1,MPI_DOUBLE,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,&status);

sender = status.MPI_SOURCE;

result_Tag = status.MPI_TAG;

c[result_Tag] = result;//依据标记tag更新某行

//检查是否所有行均已经发送,若没有则继续发送

if(numsend<rows)

{

    for(j=0;j<cols;j++)

buf[j] = A[numsend][j];

MPI_Send(buf,cols,MPI_DOUBLE,sender,numsend,MPI_COMM_WORLD);

    numsend++;

}

else

MPI_Send(&result,1,MPI_DOUBLE,sender,ExitTag,MPI_COMM_WORLD);


}

}


else

{

MPI_Bcast(b,rows,MPI_DOUBLE,master,MPI_COMM_WORLD);

while(1)

{

MPI_Recv(buf,cols,MPI_DOUBLE,master,MPI_ANY_TAG,MPI_COMM_WORLD,&status);


result_row = status.MPI_TAG;

if(result_row == ExitTag)

break;

result = 0.0;

for(j=0;j<cols;j++)

result += buf[j]*b[j];


MPI_Send(&result,1,MPI_DOUBLE,master,result_row,MPI_COMM_WORLD);


}

}


MPI_Barrier(MPI_COMM_WORLD);//同步

if(myid == master)

{

fprintf(stderr,"***start***");

fprintf(stderr,"\nMatrix A:\n");

for(i=0;i<rows;i++)

{

for(j=0;j<cols;j++)

fprintf(stderr,"%.2f\t",A[i][j]);

fprintf(stderr,"\n");

}

fprintf(stderr,"\nVector b:\n");

for(i=0;i<rows;i++)

fprintf(stderr,"%.2f\t",b[i]);


fprintf(stderr,"\n");

fprintf(stderr,"\nVector c:\n");

for(i=0;i<rows;i++)

fprintf(stderr,"%.2f\t",c[i]);


fprintf(stderr,"\n***end***\n");

}

MPI_Finalize();

}

[/code]


运行结果

[root@dl1 mpi]#  mpirun -np 2 ./test-8-2-1
Process 1 in 2
Process 0 in 2
***start***
Matrix A:
0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
1.00    1.00    1.00    1.00    1.00    1.00    1.00    1.00    1.00    1.00
2.00    2.00    2.00    2.00    2.00    2.00    2.00    2.00    2.00    2.00
3.00    3.00    3.00    3.00    3.00    3.00    3.00    3.00    3.00    3.00
4.00    4.00    4.00    4.00    4.00    4.00    4.00    4.00    4.00    4.00
5.00    5.00    5.00    5.00    5.00    5.00    5.00    5.00    5.00    5.00
6.00    6.00    6.00    6.00    6.00    6.00    6.00    6.00    6.00    6.00
7.00    7.00    7.00    7.00    7.00    7.00    7.00    7.00    7.00    7.00
8.00    8.00    8.00    8.00    8.00    8.00    8.00    8.00    8.00    8.00
9.00    9.00    9.00    9.00    9.00    9.00    9.00    9.00    9.00    9.00

Vector b:
1.00    1.00    1.00    1.00    1.00    1.00    1.00    1.00    1.00    1.00

Vector c:
0.00    10.00   20.00   30.00   40.00   50.00   60.00   70.00   80.00   90.00
***end***


2.2 主进程打印各从进程的消息

#include "mpi.h"

#include <stdio.h>


#define MSG_EXIT 1

#define MSG_PRINT_ORDERED 2             //按序打印标志

#define MSG_PRINT_UNORDERED 3   //乱序打印标志

#define MSG_MAX_LENGTH 256              //消息最大长度


void MasterIO(void) //主进程执行的部分

{

int i,size,nSlave,firstMsg;

char buf[MSG_MAX_LENGTH],buf2[MSG_MAX_LENGTH];

MPI_Status status;


MPI_Comm_size(MPI_COMM_WORLD,&size);

nSlave=size-1; //从进程的进程数

while(nSlave>0) //只要还有从进程,则执行接收与打印

{

MPI_Recv(&buf,MSG_MAX_LENGTH,MPI_CHAR,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,&status);

switch(status.MPI_TAG)

{

case MSG_EXIT://若从进程要求退出

nSlave--; //总的从进程个数减1

break;

case MSG_PRINT_UNORDERED: //要求乱序打印

fputs(buf,stdout); //直接将该消息打印

break;

case MSG_PRINT_ORDERED: //要求按序打印

firstMsg=status.MPI_SOURCE;

for(i=1;i<size;i++) //标识号从小到大开始打印

{

if(i==firstMsg) //若接收到的消息恰巧是需要打印的消息

fputs(buf,stdout); //直接打印

else //否则先接收需要打印的消息,然后再打印

{

MPI_Recv(&buf2,MSG_MAX_LENGTH,MPI_CHAR,i,MSG_PRINT_ORDERED,MPI_COMM_WORLD,&status);

fputs(buf2,stdout);

}

}

break;

}

}

}


void SlaveIO(void) //从进程执行的部分

{

char buf[MSG_MAX_LENGTH];

int myid;


MPI_Comm_rank(MPI_COMM_WORLD,&myid);


sprintf(buf,"Hello from slave %d,ordered print\n",myid);

MPI_Send(buf,strlen(buf)+1,MPI_CHAR,0,MSG_PRINT_ORDERED,MPI_COMM_WORLD); //向主进程发送一个有序打印消息


sprintf(buf,"Goodbye from slave %d,ordered print\n",myid);

MPI_Send(buf,strlen(buf)+1,MPI_CHAR,0,MSG_PRINT_ORDERED,MPI_COMM_WORLD); //向主进程发送一个有序打印消息


sprintf(buf,"I'm exiting (%d) ,unordered print\n",myid);

MPI_Send(buf,strlen(buf)+1,MPI_CHAR,0,MSG_PRINT_UNORDERED,MPI_COMM_WORLD); //向主进程发送一个乱序打印消息


MPI_Send(buf,0,MPI_CHAR,0,MSG_EXIT,MPI_COMM_WORLD); //向主进程发送退出执行的消息

}


void main(int argc,char* argv[])

{

int myid;


MPI_Init(&argc,&argv);

MPI_Comm_rank(MPI_COMM_WORLD,&myid);

if(myid==0)

MasterIO();

else

SlaveIO();

MPI_Finalize();

}

[/code]


运行结果

[root@dl1 mpi]#  mpirun -np 4 ./test-8-2-2
Hello from slave 1,ordered print
Hello from slave 2,ordered print
Hello from slave 3,ordered print
Goodbye from slave 1,ordered print
Goodbye from slave 2,ordered print
Goodbye from slave 3,ordered print
I'm exiting (1) ,unordered print
I'm exiting (2) ,unordered print
I'm exiting (3) ,unordered print
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: