文章目錄
基本知識
數據類型
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;
}