阻塞型通信函数需要等待指定的操作实际完成,或所涉及的数据被MPI系统安全备份后才返回,如MPI_Send和MPI_Recv函数
非阻塞型通信函数总是立刻返回,实际操作由mpi后台进行完成,需要调用其它函数来查询通信是否完成,如MPI_ISend和MPI_IRecv函数
是一个进程组中的所有进程都参加的全局通信操作,按照通信方向的不同,集合通信分为三种类型:
1.一对多:一个进程向其它所有的进程发送消息,这个负责发送消息的进程叫做Root进程。
2.多对一:一个进程负责从其它所有的进程接收消息,这个接收的进程也叫做Root进程。
3.多对多:每一个进程都向其它所有的进程发送或者接收消息。
接下来记录一些集合通信函数
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号进程有最后的结果。
int MPI_Alleduce(
void* input_data_p,//发送地址
void* output_data_p,//接收地址
int count,//数量
MPI_Datatype datatype,//类型
MPI_Op operator,//要进行什么操作,见课本表3-2
MPI_Comm comm);//通信子
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,也就是每个进程分得的行数数