MPI基本函数使用


基本知识

数据类型

MPI数据类型 C语言数据类型
MPI_CHAR
MPI_SHORT signed short int
MPI_INT signed int
MPI_LONG signed long int
MPI_LONG_LONG signed long long int
MPI_UNSIGNED_CHAR unsigned char
MPI_UNSIGNED_SHORT unsigned short int
MPI_UNSIGNED unsigned int
MPI_UNSIGNED unsigned long int
MPI_FLOAT float
MPI_DOUBLE double
MPI_LONG_DOUBLE long double
MPI_BYTE
MPI_PACKED

运算

#define MPI_OP_NULL ((MPI_Op)0x18000000)
#define MPI_MAX     ((MPI_Op)0x58000001)
#define MPI_MIN     ((MPI_Op)0x58000002)
#define MPI_SUM     ((MPI_Op)0x58000003)
#define MPI_PROD    ((MPI_Op)0x58000004)
#define MPI_LAND    ((MPI_Op)0x58000005)// 逻辑与
#define MPI_BAND    ((MPI_Op)0x58000006)// 按位与
#define MPI_LOR     ((MPI_Op)0x58000007)
#define MPI_BOR     ((MPI_Op)0x58000008)
#define MPI_LXOR    ((MPI_Op)0x58000009)
#define MPI_BXOR    ((MPI_Op)0x5800000a)
#define MPI_MINLOC  ((MPI_Op)0x5800000b)// 求最小值所在位置
#define MPI_MAXLOC  ((MPI_Op)0x5800000c)// 求最大值所在位置
#define MPI_REPLACE ((MPI_Op)0x5800000d)

各种函数


处理器名称

// 返回调用时调用所在的处理器名。
int MPI_Get_processor_name ( char *name, int *resultlen)
	char *name : 实际节点的唯一说明字;
	int *resultlen:在name中返回结果的长度;

运行时间

在MPI编程我们可以使用MPI_Wtime函数在并行代码中计算运行时间,用MPI_Wtick来查看精度。

/*
double MPI_Wtime(void)
double MPI_Wtick(void)
MPI_WTIME返回一个用浮点数表示的秒数, 它表示从过去某一时刻到调用时刻所经历的时间。

MPI_WTICK返回MPI_WTIME的精度,单位是秒,可以认为是一个时钟滴答所占用的时间。
*/
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	double start, finish;
	
	MPI_Init(&argc, &argv);
    start = MPI_Wtime();
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);

	//your code here
	finish = MPI_Wtime();
	printf("The precision is:%lf\n",MPI_Wtick());
	//your code here
	
	printf("Hello World!I'm rank %d of %d, running %f seconds.\n", myid, numprocs, finish-start);

	MPI_Finalize();
	return 0;
}

MPI_Barrier同步

希望保证所有进程中并行代码在某个地方同时开始运行,或者在某个函数调用结束之前不能返回。

/*
int MPI_Barrier(MPI_Comm comm)
MPI_Comm comm : 通信子;

阻止调用直到communicator中所有进程已经完成调用,就是说,任意一次进程的调用只能在所有communicator中的成员已经开始调用之后进行。
*/

#include<stdio.h>
#include<mpi.h>
int main(int argc, char **argv)
{
	int myid, numprocs;
	double start, finish;
	
	MPI_Init(&argc, &argv);
    MPI_Comm comm;
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);

	//your code here
	//
	MPI_Barrier(MPI_COMM_WORLD);
	//end of your code 
	
	start = MPI_Wtime();
	
	printf("The precision is: %f\n", MPI_Wtick());
	
	finish = MPI_Wtime();
	
	printf("Hello World!I'm rank %d of %d, running %f seconds.\n", myid, numprocs, finish-start);

	MPI_Finalize();
	return 0;
}

消息传递Send|Recv

最基本的发送/接收函数都是以缓冲区作为端点,通过参数配置来完成指定操作。

int MPI_Send(void* msg_buf_p, int msg_size, MPI_Datatype msg_type, int dest, int tag, MPI_Comm communicator)

		void* msg_buf_p : 发送缓冲区的起始地址;
		int buf_size : 缓冲区大小;
		MPI_Datatype msg_type : 发送信息的数据类型;
		int dest :目标进程的id值;
		int tag : 消息标签;
		MPI_Comm communicator : 通信子;

int MPI_Recv(void* msg_buf_p, int buf_size, MPI_Datatype msg_type, int source, int tag, MPI_Comm communicator, MPI_Status *status_p)
		
		void* msg_buf_p : 缓冲区的起始地址;
		int buf_size : 缓冲区大小;
		MPI_Datatype msg_type : 发送信息的数据类型;
		int dest :目标进程的id值;
		int tag : 消息标签;
		MPI_Comm communicator : 通信子;
		MPI_Status *status_p : status_p对象,包含实际接收到的消息的有关信息
	
#include <stdio.h>
#include <mpi.h>
#include <string.h>

int main(int argc, char **argv)
{
	int myid, numprocs, source;
	MPI_Status status;
	char message[100];

	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    
    if(myid != 0) {
    	strcpy(message, "hello world!");
    	//your code here
    	MPI_Send(message,strlen(message),MPI_CHAR,0,99,MPI_COMM_WORLD);
    	//end of your code
	}
	else { //myid == 0
		for(source=1; source<numprocs; source++) {
			//your code here
			MPI_Recv(message,100,MPI_CHAR,source,99,MPI_COMM_WORLD,&status);
			//end of your code
			
			printf("%s\n", message);
		}
	}

	MPI_Finalize();
	return 0;
}

地址偏移量

#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	MPI_Aint address1, address2, address3;
	int a, b, c, dist1, dist2;
	a = 1;
	b = 2;
	c = 3;
	
	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	// your code here
    MPI_Address(&a,&address1);
    MPI_Address(&b,&address2);
    MPI_Address(&c,&address3);
	// end of your code
	
	dist1 = address2 - address1 ;
	dist2 = address3 - address1 ;

	if(myid == 0) {
		printf("The distance between a and b is %d\n", dist1);
		printf("The distance between a and c is %d\n", dist2);
	}
	MPI_Finalize();
	return 0;
}

数据打包、解包Pack|Unpack

我们希望将不连续的数据或是不相同的数据类型的数据一起发送到其他进程,而不是效率很低地逐个发送。一个解决这个问题的方案是将数据封装成包,再将数据包放到一个连续的缓冲区,发送到接收缓冲区后再提取出来尽心解包。

int MPI_Pack(void* inbuf, int incount, MPI_datatype datatype, void *outbuf, int outcount, int *position, MPI_Comm comm) 

		void* inbuf : 输入缓冲区地址;
		int incount :输入数据项数目;
		MPI_datatype datatype :数据项的类型;
		void *outbuf :输出缓冲区地址;
		int outcount :输出缓冲区大小;
		int *position :缓冲区当前位置;
		MPI_Comm comm :通信子;

int MPI_Unpack(void* inbuf, int insize, int *position, void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm) 

		void* inbuf : 输入缓冲区地址;
		int insize :输入数据项数目;
		MPI_datatype datatype :数据项的类型;
		void *outbuf :输出缓冲区地址;
		int outcount :输出缓冲区大小;
		int *position :缓冲区当前位置;
		MPI_Comm comm :通信子;
#include <stdio.h>
#include <mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs, source;
	MPI_Status status;
	int i, j, position;
	int k[2];
	int buf[1000];

	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    
    i = 1;
    j = 2;
    
    if(myid == 0) {
        
        position = 0 ;
        
    	// your code here
    	MPI_Pack(&i,1,MPI_INT,buf,1000,&position,MPI_COMM_WORLD);
    	MPI_Pack(&j,1,MPI_INT,buf,1000,&position,MPI_COMM_WORLD);
    	// end of your code
    	
    	MPI_Send(buf, position, MPI_PACKED, 1, 99, MPI_COMM_WORLD); 
	}
	else if (myid == 1){ 
		MPI_Recv(k, 2, MPI_INT, 0, 99, MPI_COMM_WORLD, &status);
		
		position = 0 ;
		
		MPI_Unpack(k, 2, &position, &i, 1, MPI_INT, MPI_COMM_WORLD);
		MPI_Unpack(k, 2, &position, &j, 1, MPI_INT, MPI_COMM_WORLD);
		
		printf("The number is %d and %d", i, j);
	}

	MPI_Finalize();
	return 0;
}

MPI_Reduce规约

我们常常需要对于数据做同一种操作,并将结果返回到指定的进程中,这个过程称为集合通信。例如,将数据分散到各个进程中,先在各个进程内进行求和,再在全局完成求和-平均这个操作,这个过程是一个规约的过程。

    /*your code here
    int MPI_Reduce(void * input_data_p, void * output_data_p, int count, MPI_Datatype datatype, MPI_Op operator, int dest_process, MPI_Comm comm)
    void * input_data_p : 每个进程的待处理数据存放在input_data_p中; 
	void * output_data_p : 存放最终结果的目标进程的地址;
	int count : 缓冲区中的数据个数;
	MPI_Datatype datatype : 数据项的类型;
	MPI_Op operator : 操作子,例如加减;
	int dest_process : 目标进程的编号;*/
#include <stdio.h>
#include <mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	double local_num = 3.0; 

	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
	MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    
    double global_num;
    

    MPI_Reduce((void*)&local_num,(void*)&global_num,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD);
    //end of your code
    
    if(myid == 0) {
    	printf("Total sum = %f, avg = %f\n", global_num, global_num / numprocs);
	}

	MPI_Finalize();
	return 0;
}

MPI_Bcast广播

如果属于一个进程的数据被发送到通信子中的所有进程,这样的集合通信叫做广播。

int MPI_Bcast(void* buffer, int count, MPI_Datatype datatype, int source, MPI_Comm comm)
// 广播函数,从一个id值为source的进程将一条消息广播发送到通信子内的所有进程,包括它本身在内。
		void*  buffer    缓冲区的起始地址; 
		int   count     缓冲区中的数据个数; 
		MPI_Datatype datatype   缓冲区中的数据类型; 
		int   source     发送信息的进程id; 
		MPI_Comm comm      通信子;
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	int source = 0;
	int array[5]={0,0,0,0,0};
	int i;
	
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
	MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    
    if(myid == source) {
        for(i = 1; i <= 5; i++)
            array[i] = i;
    }
    
    //your code here
    MPI_Bcast(array,5,MPI_INT,myid,MPI_COMM_WORLD);
    //end of your code
    
    if(myid != source) {
    	printf("In process %d, ", myid);
        for(i = 0; i < 5; i++)
            printf("arr[%d]=%d\t", i, array[i]);
        printf("\n");
	}

	MPI_Finalize();
	return 0;
}

MPI_Gather收集

有时候我们希望在一个进程中从所有进程获取信息,例如将所有进程中的一个数组都收集到根进程中作进一步的处理,这样的集合通信我们叫做收集.
收集函数,根进程(目标进程)从所有进程(包括它自己)收集发送缓冲区的数据,根进程根据发送这些数据的进程id将它们依次存放到自已的缓冲区中.

/*
int MPI_Gather(void* sendbuf, int sendcount, MPI_Datatype sendtype, 
               void* recvbuf, int recvcount, MPI_Datatype recvtype, 
               int root, MPI_Comm comm)
		void* sendbuf    发送缓冲区的起始地址
		int sendcount    发送缓冲区的数据个数
		MPI_Datatype sendtype    发送缓冲区的数据类型
		void* recvbuf    接收缓冲区的起始地址
		int recvcount    待接收的元素个数//注意该参数表示的是从单个进程接收的数据个数,不是总数
		MPI_Datatype recvtype    接收的数据类型
		int root    接收进程id 
		MPI_Comm comm    通信子
*/
#include<stdio.h>
#include <stdlib.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	int dest = 0;
	int array[5]={1,2,3,4,5};
	int *rbuf; 
	int i,j;

	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
	MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    
    if(myid == dest) {
    	rbuf=(int *)malloc(numprocs*5*sizeof(int));
	}

	//your code here
	MPI_Gather(array,5,MPI_INT,rbuf,5,MPI_INT,dest,MPI_COMM_WORLD);
	//end of your code
	
	if(myid == dest) {
		for(i=dest+1;i<numprocs;i++) {
			printf("Now is process %d's data: ", i);
			for(j=0;j<5;j++) {
				printf("array[%d]=%d\t", j, rbuf[i*5+j]);
			}
			printf("\n");
		}
	}
	
	MPI_Finalize();
	return 0;
} 

MPI_Scatter散发

在前面我们学习了收集(gather)操作,那么与之相对应也有一个相反的集合通信操作,即根进程向所有进程发送缓冲区的数据,称为散发。

需要特别说明的是,散发操作和广播操作的区别在于发送到各个进程的信息可以是不同的。

MPI_SCATTER是MPI_GATHER的逆操作,另外一种解释是根进程通过MPI_Send发送一条消息,这条消息被分成n等份,第i份发送给组中的第i个处理器, 然后每个处理器如上所述接收相应的消息。

/*
int MPI_Scatter(void* sendbuf, int sendcount, MPI_Datatype sendtype,
                void* recvbuf, int recvcount, MPI_Datatype recvtype,
                int root, MPI_Comm comm)
		void* sendbuf   发送缓冲区的起始地址
		int sendcount   发送的数据个数
		MPI_Datatype sendtype    发送缓冲区中的数据类型
		void* recvbuf     接收缓冲区的起始地址
		int recvcount  待接收的元素个数
		MPI_Datatype recvtype    接收的数据类型
		int root       发送进程id
		MPI_Comm comm       通信子
*/
#include<stdio.h>
#include<mpi.h>
#include<stdlib.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	int source = 0;
	int *sbuf;
	int rbuf[5]; 
	int i;
	
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
	MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    
    if(myid == source) {
    	sbuf=(int *)malloc(numprocs*5*sizeof(int));
    	
    	for(int i=0;i<numprocs*5;i++) {
    		sbuf[i]=i;
		}
	}

    // your code here
	MPI_Scatter(sbuf,5,MPI_INT,rbuf,5,MPI_INT,source,MPI_COMM_WORLD);
	// end of your code
	
	printf("Now is process %d: ", myid);
	for(i=0;i<5;i++) {
		printf("array[%d]=%d\t", i, rbuf[i]);
	}
	printf("\n");
	MPI_Finalize();
	return 0;
} 


组的管理

组是一个进程的有序集合,在实现中可以看作是进程标识符的一个有序集。组内的每个进程与一个整数rank相联系,序列号从0开始并且是连续的。我们可以在通信组中使用组来描述通信空间中的参与者并对这些参与者进行分级(这样在通信空间中为它们赋予了唯一的名字)。

由此可见,组是我们对进程集合更高一级的抽象,我们可以在组的基础上对各个进程进行更进一步的操作,例如通过虚拟拓扑来辅助并行操作的实现。

在这里我们先介绍两个特殊的预定义组,MPI_GROUP_EMPTY和MPI_GROUP_NULL。
需要特别说明的是,前者是一个空组的有效句柄,可以在组操作中作为一个参数使用;而后者是一个无效句柄,在组释放时会被返回。

创建

/*
int MPI_Comm_group(MPI_Comm comm, MPI_Group *group)
int MPI_Group_rank(MPI_Group group, int *rank)
*/
#include<stdio.h>
#include<mpi.h>
int main(int argc, char **argv)
{
	int myid, numprocs;
	MPI_Group group_world;
	int rank_of_group;
	
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	
	// your code here
	MPI_Comm_group(MPI_COMM_WORLD,&group_world);
	MPI_Group_rank(group_world,&rank_of_group);
	// end of your code
	
	printf("rank: %d\n", rank_of_group);
	MPI_Finalize();
	return 0;
}

/*基于已经存在的进程组创建一个新的组,并指明被包含(included)其中的成员进程。
int MPI_Group_incl(MPI_Group old_group, int count, int *members, MPI_Group *new_group)

MPI_Group old_group : 旧进程组;
int count : members数组中元素的个数;
int *members : 旧进程组中需要放入新进程组的进程的编号;
MPI_Group *new_group : 新进程组;

基于与初始通信子MPI_COMM_WORLD相联系的组创建一个新的组,这个新的组的成员是通信者MPI_COMM_WORLD的奇数编号的进程。
*/
#include<stdio.h>
#include<mpi.h>
int main(int argc, char **argv)
{
	int myid, numprocs, odd_rank;
	MPI_Group group_world, odd_group;
	int i;
	int members[10];
		
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	
	MPI_Comm_group(MPI_COMM_WORLD, &group_world);

	for(i=0; i<numprocs/2; i++) {
		members[i] = 2*i+1 ;
	}
	
	// your code here
	MPI_Group_incl(group_world,numprocs/2,members,&odd_group);
	// end of your code
	
	MPI_Group_rank(odd_group, &odd_rank);
	printf("In process %d: odd rank is %d\n", myid, odd_rank);
	MPI_Finalize();
	return 0;
} 

/*
基于已经存在的进程组创建一个新的组,并指明不被包含(excluded)其中的成员进程。

int MPI_Group_excl(MPI_Group old_group, int count, int *nonmembers, MPI_Group *new_group)

MPI_Group old_group : 旧进程组;
int count : nonmembers数组中元素的个数;
int *nonmembers : 旧进程组中不需要放入新进程组的进程的编号;
MPI_Group *new_group : 新进程组;
*/
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs, even_rank;
	MPI_Group group_world, even_group;
	int i;
	int nonmembers[10];
		
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	
	MPI_Comm_group(MPI_COMM_WORLD, &group_world);

	for(i=0; i<numprocs/2; i++) {
		nonmembers[i] = 2*i+1 ;
	}
	
	// your code here
	MPI_Group_excl(group_world,numprocs/2,nonmembers,&even_group);
	// end of your code
	
	MPI_Group_rank(even_group, &even_rank);

	printf("In process %d: even rank is %d\n", myid, even_rank);
	MPI_Finalize();
	return 0;
} 

组的比较

/*对两个进程组做最基本的判断,例如成员是否相同,次序是否一致等等。
int MPI_Group_compare(MPI_Group group1, MPI_Group group2, int *result)

MPI_Group group1 : 要比较的组1;
MPI_Group group2 : 要比较的组2;
int *result:结果;
如果在两个组中成员和次序完全相等,返回MPI_IDENT。
例如在group1和group2是同一句柄时就会发生这种情况。
如果组成员相同而次序不同则返回MPI_SIMILAR,
否则返回MPI_UNEQUAL。
*/
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	MPI_Group group_world, new_group_world;
	int members[5];
	int result;
	
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	
	MPI_Comm_group(MPI_COMM_WORLD, &group_world);
	
	members[0] = 0 ;
	
	MPI_Group_incl(group_world, 1, members, &new_group_world);
	
	if(myid == 0) {
		// your code here
		MPI_Group_compare(group_world,group_world,&result);
		// end of your code
		
		if (result == MPI_IDENT) {
			printf("Now the groups are identical.\n");
		}
		else if (result == MPI_SIMILAR) {
			printf("Now the groups are similar.\n");
		}
		else {
			printf("Now the groups are unequal.\n");
		}
		
		// your code here
		MPI_Group_compare(group_world,new_group_world,&result);
		// end of your code
		
		if (result == MPI_IDENT) {
			printf("Now the groups are identical.\n");
		}
		else if (result == MPI_SIMILAR) {
			printf("Now the groups are similar.\n");
		}
		else {
			printf("Now the groups are unequal.\n");
		}
	}

	MPI_Finalize();
	return 0;
}


相对编号

如果知道了在组MPI_COMM_WORLD中某些进程的编号,如何根据这些编号来操作在不同组的同一进程来完成不同的任务呢?

/*
int MPI_Group_translate_ranks(MPI_Group group1, int count, int *ranks1, MPI_Group group2, int *ranks2)

MPI_Group group1 : 进程组1;
MPI_Group group2 : 进程组2;
int count : ranks1和ranks2数组中元素的个数;
int *ranks1 : 进程组1中有效编号组成的数组;
int *ranks2 : ranks1中的元素在进程组2中的对应编号

检测两个不同组中相同进程的相对编号。如果属于进程组1的某个进程可以在ranks1中找到,而这个进程不属于进程组2,则在ranks2中对应ranks1的位置返回值为MPI_UNDEFINED。
*/
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	MPI_Group group_world, group1, group2;
	int i;
	int ranks1[10];
	int ranks2[10];
	int ranks_output[10];
		
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	
	MPI_Comm_group(MPI_COMM_WORLD, &group_world);

	for(i=0; i<numprocs-1; i++) {
		ranks1[i] = i ;
		ranks2[i] = i+1 ;
	}
	
	MPI_Group_incl(group_world, numprocs-1, ranks1, &group1);
	MPI_Group_incl(group_world, numprocs-1, ranks2, &group2);
	
	// your code here
	MPI_Group_translate_ranks(group1,numprocs-1,ranks1,group2,ranks2);
	// end of your code
	
	if (myid == 0) {
		for (i=0; i<numprocs-1; i++) {
			printf("The rank in group2 is: %d\n", ranks_output[i]);
		}
	}

	MPI_Finalize();
	return 0;
}

集合类操作

对于两个集合,我们经常对其进行各种各样的集合操作,例如交/并。
MPI同样提供了对组的集合类操作。

int MPI_Group_union(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup)

int MPI_Group_intersection(MPI_Group group1,MPI_Group group2,MPI_Group *newgroup) 

int MPI_Group_difference(MPI_Group group1,MPI_Group group2,MPI_Group *newgroup)

MPI_Group group1 : 要操作的组1;
MPI_Group group2 : 要操作的组2;
MPI_Group *newgroup:新的组;
/*将组按照编号的奇偶分为两个新的组,再用并操作将它们合起来,输出各个进程在新的组的编号。

输出结果:
In process 0: union rank is 2
In process 1: union rank is 0
In process 2: union rank is 3
In process 3: union rank is 1
*/
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs, union_rank;
	MPI_Group group_world, odd_group, even_group, union_group;
	int i;
	int members[10];
	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	
	MPI_Comm_group(MPI_COMM_WORLD, &group_world);

	for(i=0; i<numprocs/2; i++) {
		members[i] = 2*i+1 ;
	}
	MPI_Group_incl(group_world, numprocs/2, members, &odd_group);
	MPI_Group_excl(group_world, numprocs/2, members, &even_group);
	
	// your code here
	MPI_Group_union(odd_group, even_group,&union_group);
	// end of your code
	
	MPI_Group_rank(union_group, &union_rank);
	printf("In process %d: union rank is %d\n", myid, union_rank);
	MPI_Finalize();
	return 0;
} 

释放

/* int MPI_Group_free(MPI_Group *group)

调用函数会标记一个被释放的组对象,组句柄被调用置为MPI_GROUP_NULL。
任何正在使用此组的操作将正常完成
*/
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	MPI_Group group_world;
	int size0;
	
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	
	MPI_Comm_group(MPI_COMM_WORLD, &group_world);
	
	MPI_Group_size(group_world, &size0);
	
	if(myid == 0) {
		printf("Now the size is %d\n", size0);
	}
	
	// your code here
	MPI_Group_free(&group_world);
	// end of your code
	
	if(myid == 0) {
		if (group_world == MPI_GROUP_NULL)
			printf("Now the group is freed.\n");
	}

	MPI_Finalize();
	return 0;
}


通信子

我们经常使用系统帮助我们创建的初始组内通信子MPI_COMM_WORLD作为通信子的输入。

其实,还有两个系统默认创建的通信子,一个是COMM_SELF,另一个是COMM_NULL
COMM_SELF仅仅包含了当前进程,而COMM_NULL则什么进程都没有包含。

在通信子的创建中,需要特别注意的是MPI中有一个"鸡生蛋, 蛋生鸡"的特点,即所有MPI通信子的创建都是由基础通信子,即MPI_COMM_WORLD(是在MPI的外部被定义的),创建的。而这些被创建的通信子又可以作为新的通信子创建的基础。

这个模型是经过讨论后确定的,目的是为了提高用MPI写程序的安全性。

复制

/*
int MPI_Comm_dup(MPI_Comm comm,MPI_Comm *newcomm)

MPI_Comm comm : 旧的通信子;
MPI_Comm *newcomm : 新的通信子;

复制一个新的通信子,需要特别说明的是,结果显示
MPI_IDENT表示上下文(context)和组(group)都相同,
MPI_CONGRUENT表示上下文不同(different)但组完全相同(identical),
MPI_SIMILAR表示上下文不同,组的成员相同但次序不同(similar),
否则就是MPI_UNEQUAL。
*/
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	MPI_Comm new_comm; 
	int result;
	
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    
    // your code here
	MPI_Comm_dup(MPI_COMM_WORLD,&new_comm);
	// end of your code
		
	MPI_Comm_compare(MPI_COMM_WORLD, new_comm, &result);
	
	if(myid == 0) {
		
		if ( result == MPI_IDENT) {
			printf("The comms are identical.\n");
		}
		else if ( result == MPI_CONGRUENT ) {
			printf("The comms are congruent.\n");
		}
		else if ( result == MPI_SIMILAR ) {
			printf("The comms are similar.\n");
		}
		else if ( result == MPI_UNEQUAL ) {
			printf("The comms are unequal.\n");
		}
	}

	MPI_Finalize();
	return 0;
}

创建

/* 通过组创建新的通信子
int MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm)

MPI_Comm comm : 旧的通信子;
MPI_Group group : 与comm相关联的组或其子集;
MPI_Comm *newcomm : 新的通信子;

用由 group 所定义的通信组及一个新的上下文创建了一个新的通信子newcomm。对于不在group中的进程,函数返回MPI_COMM_NULL。

复制一个新的通信子,并以此为基础创建一个新的通信子。由于示例是用奇数编号的进程来创建通信子的,所以只在奇数进程中输出结果。
*/
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs, new_numprocs;
	MPI_Group group_world, odd_group;
	MPI_Comm new_comm;
	int i;
	int members[10];
		
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	
	MPI_Comm_group(MPI_COMM_WORLD, &group_world);

	for(i=0; i<numprocs/2; i++) {
		members[i] = 2*i+1 ;
	}
	
	MPI_Group_incl(group_world, numprocs/2, members, &odd_group);
	
	// your code here
	MPI_Comm_create(MPI_COMM_WORLD,odd_group,&new_comm);
	// end of your code
	
	if (myid % 2 != 0 ) {
	    MPI_Comm_size(new_comm, &new_numprocs);
	    
		printf("The new comm's size is %d.\n", new_numprocs);
	} 
	

	MPI_Finalize();
	return 0;
} 

划分

有时候我们希望根据拓扑来创建不同的域,例如创建一个二维数组,显然一个个创建是很不方便的,这时候我们需要用到一个新的函数来进行划分。

/*
int MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm *newcomm)

MPI_Comm comm : 旧的通信子,也就是被划分的域;
int color : 子域的标识,也就是被划分出来的每个子域都对应一个color,每一个子域包含具有同样color的所有进程;
int key : 在每一个子域内, 进程按照key所定义的值的次序进行排列。
MPI_Comm *newcomm : 新的通信子;

函数将与comm相关的域划分为若干不相连的子域,根据color和key参数决定每个进程所处的位置。
*/
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	MPI_Comm row_comm, column_comm;
	int myrow, mycolumn;
	int color = 3 ;
		
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	
	myrow = myid / color ;
	mycolumn = myid % color ;

	// your code here
	MPI_Comm_split(MPI_COMM_WORLD,color,myrow,&row_comm);
	MPI_Comm_split(MPI_COMM_WORLD,color,mycolumn,&column_comm);
    // end of your code
	
	int rowsum, columnsum;
	
	rowsum = myid;
	columnsum = myid;
	
	MPI_Allreduce(MPI_IN_PLACE, &rowsum, 1, MPI_INT, MPI_SUM, row_comm);  
    MPI_Allreduce(MPI_IN_PLACE, &columnsum, 1, MPI_INT, MPI_SUM, column_comm); 
    
    printf("I'm process %d, my coordinates are (%d, %d), row sum is %d, column sum is %d\n", myid, myrow, mycolumn,  
rowsum, columnsum); 
    
	MPI_Finalize();
	return 0;
} 

释放

通信子也存在析构的操作。

值得注意的是,这个函数操作只是将句柄置为MPI_COMM_NULL,任何使用此通信子的挂起操作都会正常完成;仅当没有对此对象的活动引用时,它才会被实际撤消。

/*int MPI_Comm_free(MPI_Comm *comm)
MPI_Comm *comm : 通信子;

用由group所定义的通信组及一个新的上下文创建了一个新的通信子newcomm。对于不在group中的进程,函数返回MPI_COMM_NULL。
*/
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv)
{
	int myid, numprocs;
	MPI_Comm new_comm;
	
	MPI_Init(&argc, &argv);
	
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	
	MPI_Comm_dup(MPI_COMM_WORLD, &new_comm);
	
	// your code here
	MPI_Comm_free(&new_comm);
	// end of your code

	if(myid == 0) {
		if (new_comm == MPI_COMM_NULL)
			printf("Now the comm is freed.\n");
	}
	MPI_Finalize();
	return 0;
}




资源

MPI总结-简书
MPI实战-超算习堂

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章