《并行程序设计导论》 mpi中集合通信

通信类型

mpi点对点通信,分为阻塞型和非阻塞型blocking

阻塞型通信函数需要等待指定的操作实际完成,或所涉及的数据被MPI系统安全备份后才返回,如MPI_Send和MPI_Recv函数
非阻塞型通信函数总是立刻返回,实际操作由mpi后台进行完成,需要调用其它函数来查询通信是否完成,如MPI_ISend和MPI_IRecv函数

mpi集合通信

是一个进程组中的所有进程都参加的全局通信操作,按照通信方向的不同,集合通信分为三种类型:
1.一对多:一个进程向其它所有的进程发送消息,这个负责发送消息的进程叫做Root进程。
2.多对一:一个进程负责从其它所有的进程接收消息,这个接收的进程也叫做Root进程。
3.多对多:每一个进程都向其它所有的进程发送或者接收消息。

接下来记录一些集合通信函数

  1. MPI_Reduce函数 MPI对全局求和函数进行概括,使这些可能性中的任意一个都能用单个函数实现
int MPI_Reduce(
void* input_data_p,//发送地址
void* output_data_p,//接收地址
int count,//数量
MPI_Datatype datatype,//类型
MPI_Op operator,//要进行什么操作,见课本表3-2
int dest_process,//在哪个进程上做
MPI_Comm comm);//通信子

例如
MPI_Reduce(&local_int,&total_int,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
把所有进程中的local_int都加到total_int上,在0号进程上进行,只有0号进程有最后的结果。

  1. MPI_Allreduce函数,可以令通信子中的所有进程都存储结果
int MPI_Alleduce(
void* input_data_p,//发送地址
void* output_data_p,//接收地址
int count,//数量
MPI_Datatype datatype,//类型
MPI_Op operator,//要进行什么操作,见课本表3-2
MPI_Comm comm);//通信子
  1. MPI_Bcast函数,一个进程的数据被发送到通信子中所有的进程,广播
int MPI_Bcast(
void* data_p,
int count,
MPI_Datatype datatype,
int source_proc,
MPI_Comm comm);

进程号为source_proc的进程将data_p所引用的内存内容发送了通信子comm中的所有进程。
可以用MPI_Bcast函数,来取代MPI_Send和MPI_Recv函数

void get_input(int my_rank,int comm_sz,double* a_p,double* b_p,int* n_p)
{
	int dest;
	if(my_rank==0)
	{
		printf("enter a,b, and n\n");
		scanf("%lf %lf %d",a_p,b_p,n_p);
 		for(dest=1;dest<comm_sz;dest++)
  		{
  	 		MPI_Send(a_p,1,MPI_DOUBLE,dest,0,MPI_COMM_WORLD);
   			MPI_Send(b_P,1,MPI_DOUBLE,dest,0,MPI_COMM_WORLD);
   			MPI_Send(n_p,1,MPI_INT,dest,0,MPI_COMM_WORLD);
  		}
 	else
 	{
  		MPI_Recv(a_p,1,MPI_DOUBLE,0,0,MPI_COMM_WORLD,MPI+STATUS_IGNORE);
  		MPI_Recv(b_p,1,MPI_DOUBLE,0,0,MPI_COMM_WORLD,MPI+STATUS_IGNORE);
 		MPI_Recv(n_p,1,MPI_INT,0,0,MPI_COMM_WORLD,MPI+STATUS_IGNORE);
 	}
}

改变后为

void get_input(int my_rank,int comm_sz,double* a_p,double* b_p,int* n_p)
{
	if(my_rank==0)
	{
		printf("enter a,b,and n n");
		scanf("%lf %lf %d",a_p,b_p,n_p);
	}
	MPI_Bcast(a_p,1,MPI_DOUBLE,0,MPI_COMM_WORLD);
	MPI_Bcast(b_p,1,MPI_DOUBLE,0,MPI_COMM_WORLD);
	MPI_Bcast(n_p,1,MPI_INT,0,MPI_COMM_WORLD);
	
}

4.MPI_Scatter函数,0号进程读入整个向量,但只将分量发送给需要分量的其它进程 散射

int MPI_Scatter(
void* send_buf_p,
int send_count,//表示发送到每个进程的数据量而不是send_buf_p所引用的内存数据量
MPI_Datatype send_type,
void* recv_buf_p,
int recv_count,
MPI_Datatype recv_type,
int src_proc,
MPI_Comm comm);

如果通信子comm包含comm_sz个进程,那么MPI_Scatter函数会将send_buf_p所引用的数据分为comm_sz份,第一份给0号进程,第二份给1号进程,第三份给2号进程,以此类推。这种只适用于块划分法,并且向量的分量个数n可以整除comm_sz的情况。

void read_vector(double* local_a,int local_n,int n,char* vec_name,int my_rank,MPI_Comm comm)
{
	double* a=NULL;
	int i;
	if(my_rank==0)
	{
		a=malloc(n*sizeof(double));
		printf("enter the vector %s\n",vec_name);
		for(i=0;i<n;i++)
			scanf("%lf",&a[i]);
		MPI_Scatter(a,local_n,MPI_DOUBLE,local_a,local_n,MPI_DOUBLE,0,comm);
//全部的数据都放在a里,每次从a中取出local_n个MPI_DOUBLE型数据,发送给每个进程的local_a空间local_n个MPI_DOUBLE型数据,这些分发过程由0号进程来做			

		free(a);
	}
	else
	{
		MPI_Scatter(a,local_n,MPI_DOUBLE,local_a,local_n,MPI_DOUBLE,0,comm);
	}
}

5.MPI_Gather函数,将向量的所有分量都收集到0号进程中,然后由0号进程将所有分量打印出来。

int MPI_Gather(
void* send_buf_p,
int send_count,
MPI_Datatype send_type,
void* recv_buf_p,
int recv_count,//指的是每个进程收到的数据量
MPI_Datatype recv_type,
int dest_proc,
MPI_Comm comm);

在0号进程中,由send_buf_p所引用的内存区的数据存储在recv_buf_p的第一个块中,在1号进程中,由send_buf_p所引用的内存区的数据存储在recv_buf_p的第二个块中,以此类推。
使用MPI_Gather函数的限制与使用MPI_Scatter函数的限制是类似的,只有在使用块划分法,并且每块大小都相同的情况下,打印函数才可以正确运行。

voif print_vector(double* local_b,int local_n,int n,char* title,int my_rank,MPI_Comm comm)//n理解为进程数
{
	double* b=NULL;
	int i;
	if(my_rank==0)
	{
		b=malloc(n*sizeof(double));
		MPI_Gather(local_b,local_n,MPI_DOUBLE,b,local_n,MPI_DOUBLE,0,comm);
		printf("%s\n",title);
		for(i=0;i<n;i++)
			printf("%f",b[i]);
		printf("\n");
		free(b);
	}
	else
	{
		MPI_Gather(local_b,local_n,MPI_DOUBLE,b,local_n,MPI_DOUBLE,0,comm);
/*我是这么理解的,在除了0号进程外的进程的b只是一个地址,没有分配空间,
上面的意思是将数据传到0号进程的b的空间内,这个b是0号的,而不是上面那个double* b=NULL;*/
	}
}
		

6.MPI_Allgather函数,将每个进程的send_buf_p内容串联起来,存储到每个进程的recv_buf_p参数中

int MPI_Allgather(
void* send_buf_p,
int send_count,
MPI_Datatype send_type,
void* recv_buf_p,
int recv_count,
MPI_Datatype recv_type,
MPI_Comm comm);

MPI矩阵-向量乘法函数y=ax
这里是y,a,x都进行了块划分,原本是想对a的行进行块划分,相应的y也划分,所有的进程都得到一个完整的x

void mat_vect_mult(double* local_a,double* local_x,double* local_y,int local_m,int n,int local_n,MPI_Comm comm)
{
	double* x;
	int local_i,j;
	int local_ok=1;
	x=malloc(n*sizeof(double));
	MPI_Allgather(local_x,local_n,MPI_DOUBLE,x,local_n,MPI_DOUBLE,comm);
	//这里是涉及到每个进程,就不要分myrank?=0了
	for(local_i=0;local_i<local_m;local_i++)
	{
		local_y[local_i]=0.0;
		for(j=0;j<n;j++)
			local_y[local_i]+=local_a[local_i*n+j]*x[j];
	}
	free(x);
}
/*这里的local_m就是m/comm_sz,也就是每个进程分得的行数数
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值