MPI 函数总结(并行程序设计导论)

本篇文章只是记录api的用法和回顾,方便记忆

MPI

  1. 在程序里的任何执行点,只能连接一个用户提供的缓冲区

简单例子

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

const int MAX_STRING= 100;
int main()
{
     char  greeting[MAX_STRING];
     int comm_sz;
     int my_rank;
     int q;
     MPI_Init(NULL,NULL);
     MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
     MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);

     if(my_rank != 0)
     {
        sprintf(greeting,"Greetings from process %d of %d!",my_rank,comm_sz);
        MPI_Send(greeting,strlen(greeting)+1,MPI_CHAR,0,0,MPI_COMM_WORLD);

     }
     else
     {
        printf("Greetings from process %d of %d !\n",my_rank,comm_sz);

        for(q=1;q<comm_sz;q++)
        {
            MPI_Recv(greeting,MAX_STRING,MPI_CHAR,q,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
            printf("%s\n",greeting);
        }
     }

     MPI_Finalize();
    return 0;
}
#编译

mpicc -g -Wall -o mpi_hello mpi_hello.c

#-g :产生供gdb调试用的可执行文件
# http://www.jianshu.com/p/30ffc01380a0
#-Wall:编译后显示所有警告
#-o:输出到指定文件
MPI_Init

告知MPI系统进行所有必要的初始化设置。例如,系统可能需要为消息缓冲区分配存储空间,为进程指定进程号等。
PS:在调用MPI_Init前不应该调用其他MPI函数

#返回一个int型错误码(大部分时候忽略)
int MPI_Init(
     #指向argc的指针,不使用设置为NULL
     int *  argc_p /*in/out*/,
     #指向argv的指针,不使用设置为NULL
     char *** argv_p /*in/out*/ )
MPI_Finalize

告知MPI系统MPI已经所使用完毕,为MPI而分配的所有资源都可以释放

int MPI_Finalize(void)
Sample 1:
...
#include <mpi.h>
...
int main(int argc,char * argv[]){
MPI_Init(&argc,&argv);
...
MPI_Finalize();
return 0;
}
MPI_COMM_WORLD

通信子 是一组可以互相发送消息的进程集合。MPI_Init的其中一个目的,是在用户启动程序时,定义由用户启动的所有进程组成的通信子。
PS:一个通信子的进程所发的消息不能被另一个通信子中的进程所接收

int MPI_Comm_size(
    #通信子
    MPI_Comm comm /*in*/,
    #返回通信子的进程号
    int*  comm_sz_p /*out*/)

int MPI_Comm_rank(
    #通信子
    MPI_Comm comm /*in*/,
    #返回正在调用进程在通信子的进程号
    int*  my_rank_p /*out*/)
发送模式和缓冲发送

1.MPI为发送操作提供了四种模式:标准(standard),同步(synchronous),就绪(ready)和缓冲(buffered)。
2.不同的模式下,MPI实现决定是将消息的内容复制到自己的存储空间,还是一直阻塞到一个相匹配的接收操作被提交。

MPI_Send 标准模式

进程执行发送。

MPI标准允许以两种不同的方式来实现 :
1.简单地将消息复制到MPI设置的缓冲区并返回
2.直到对应的MPI_Recv出现前都阻塞

此外,许多MPI函数都设置了使系统从缓冲到阻塞间切换的阀值,即相对较小的消息就交由MPI_Send缓冲,但对于大型数据就选择阻塞模式。

MPI提供的缓冲机制不安全

int MPI_Send(
    #指向包含消息的内存块的指针,如greeting
    void*  msg_buf_p /*in*/,
    #指定要发送的数据量,如strlen(greeting)+1
    int  msg_size  /*in*/,
    #数据类型MPI
    MPI_Datatype  msg_type /*in*/,
    #指定要接收消息的进程号
    int  dest /*in*/,
    #tag:0用于表示消息要打印的,为1表示消息用于计算的
    int tag /*in*/,
    #通信子
    MPI_Comm  commuicator /*in*/)
MPI数据类型 C语言数据类型
MPI_CHAR signed 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
%MPI_Ssend 同步模式

1.发送操作会一直阻塞到一个相匹配的接收操作被提交。

int MPI_Ssend(
    #指向包含消息的内存块的指针,如greeting
    void*  msg_buf_p /*in*/,
    #指定要发送的数据量,如strlen(greeting)+1
    int  msg_size  /*in*/,
    #数据类型MPI
    MPI_Datatype  msg_type /*in*/,
    #指定要接收消息的进程号
    int  dest /*in*/,
    #tag:0用于表示消息要打印的,为1表示消息用于计算的
    int tag /*in*/,
    #通信子
    MPI_Comm  commuicator /*in*/)
%MPI_Rsend 就绪模式

1.在发送操作前会有一个相匹配的接收操作被提交,否则发送操作就是错误的。

int MPI_Rsend(
    #指向包含消息的内存块的指针,如greeting
    void*  msg_buf_p /*in*/,
    #指定要发送的数据量,如strlen(greeting)+1
    int  msg_size  /*in*/,
    #数据类型MPI
    MPI_Datatype  msg_type /*in*/,
    #指定要接收消息的进程号
    int  dest /*in*/,
    #tag:0用于表示消息要打印的,为1表示消息用于计算的
    int tag /*in*/,
    #通信子
    MPI_Comm  commuicator /*in*/)
%MPI_Bsend 缓冲模式

1.如果一个相关匹配的接收操作还没有被提交,那么MPI必须复制消息到本地存储空间。本地存储空间必须由用户程序提供。

int MPI_Bsend(
    #指向包含消息的内存块的指针,如greeting
    void*  msg_buf_p /*in*/,
    #指定要发送的数据量,如strlen(greeting)+1
    int  msg_size  /*in*/,
    #数据类型MPI
    MPI_Datatype  msg_type /*in*/,
    #指定要接收消息的进程号
    int  dest /*in*/,
    #tag:0用于表示消息要打印的,为1表示消息用于计算的
    int tag /*in*/,
    #通信子
    MPI_Comm  commuicator /*in*/)
%MPI_Buffer_attach

函数MPI_Bsend使用的缓冲区必须通过调用该函数来指定。

int MPI_Buffer_attach(
    #指向用户程序分配的一块内存空间的指针
    void* buffer /*in*/,
    #这块空间以字节为单位的大小
    int buffer_size /*in*/)
%MPI_Buffer_detach
  1. 函数MPI_Bsend使用的缓冲区必须通过调用该函数来指定。
  2. 调用MPI_Buffer_detach会阻塞直到buffer中的消息被传送完。
int MPI_Buffer_detach(
    #返回的时之前分配的块的内存的地址
    void* buf_p /*out*/,
    #内存块的大小
    int* buffer_size_p /*out*/)
%MPI_Pack_size

1.被传送的数据需要的存储空间大小,可以通过调用MPI_Pack_size来计算。
2.输出的参数给出了一条消息所需空间大小的上界。但仍然不够,一条消息还需要存储包括目的地,标志,通信子等信息,所以对于每条消息,都需要多余的开销。MPI针对这些增加的开销给出了上界常数:MPI_BSEND_OVERHEAD。

int MPI_Pack_size(
    #元素个数
    int count /*in*/,
    #元素类型
    MPI_Datatype datatype /*in*/,
    #通信子
    MPI_Comm comm /*in*/,
    #计算的缓存区大小
    int* size_p /*out*/,
Sample 11:
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#define SIZE 6
int main(int argc,char* argv[])
{
        int my_rank;
        int comm_sz;
        int src=0;
        int dest=1;
        int i,bsize,tsize;
        int buffer[SIZE];
        char* tmpbuffer;
        int* tmpbuf;
        int* recv_b;

        MPI_Init(NULL,NULL);
        MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);
        MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
        if(my_rank == src)
        {
            for(i=0;i<SIZE;i++)
                buffer[i]=i;

            MPI_Pack_size(SIZE,MPI_INT,MPI_COMM_WORLD,&bsize);
            tmpbuffer =(char*) malloc(bsize+MPI_BSEND_OVERHEAD);
            MPI_Buffer_attach(tmpbuffer,bsize+MPI_BSEND_OVERHEAD);

            for(dest=1;dest<comm_sz;dest++)
            {
                MPI_Bsend(buffer,SIZE,MPI_INT,dest,2000,MPI_COMM_WORLD);
                buffer[dest]=buffer[dest]*2;

            }
            MPI_Buffer_detach(&tmpbuf,&tsize);


        }
        else
        {
            /*
            if(my_rank==1)
            {
                int index=0;
                while(index<3e8)
                {
                        index++;
                }
            }*/
            recv_b=malloc(SIZE*sizeof(int));
            MPI_Recv(recv_b,SIZE,MPI_INT,src,2000,MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            //printf("%d ------        oook\n",my_rank);
            for(i=0;i<SIZE;i++)
                printf("%d -> %d\n",my_rank,recv_b[i]);
            printf("----------->\n");
        }
        MPI_Finalize();
        return 0;
}

MPI_Recv

进程接收消息

int MPI_Recv(
    #指向内存块
    void*  msg_buf_p /*out*/,
    #指定内存块要存储对象的数据量
    int  buf_size  /*in*/,
    #对象的类型
    MPI_Datatype  buf_type /*in*/,
    #接受源
    int source /*in*/,
    #tag:0用于表示消息要打印的,为1表示消息用于计算的,要与发送的消息相匹配
    int tag /*in*/,
    #通信子
    MPI_Comm  commuicator /*in*/,
    #特殊MPI常量MPI_STATUS_IGNORE
    MPI_Status* status_p /*out*/)
MPI_Sendrecv

调用一次这个函数,分别执行一次阻塞式消息发送一次消息接收。它的有用之处,实现了通信调度,使程序不再挂起和崩溃。

int MPI_Sendrecv(
    #指向包含消息的内存块的指针
    void* send_buf_p /*in*/,
    #指定要发送的数据量
    int send_buf_size /*in*/,
    #数据类型MPI
    MPI_Datatype send_buf_type /*in*/,
    #指定要接收消息的进程号(可以与source相同也可以不相同,一般相同,代表向源发送,再向源接收)
    int dest /*in*/,
    #tag:0用于表示消息要打印的,为1表示消息用于计算的
    int send_tag /*in*/,
    #指向内存块
    void* recv_buf_p /*out*/,
    #指定内存块要存储对象的数据量
    int recv_buf_size /*in*/,
    #对象的类型
    MPI_Datatype recv_buf_type /*in*/,
    #接受源(可以与dest相同也可以不相同,一般相同,代表向源发送,再向源接收)
    int source /*in*/,
    #tag:0用于表示消息要打印的,为1表示消息用于计算的,要与发送的消息相匹配
    int recv_tag /*in*/,
    #通信子
    MPI_Comm  commuicator /*in*/,
    #特殊MPI常量MPI_STATUS_IGNORE
    MPI_Status* status_p /*in*/)
MPI_Sendrecv_replace

如果发送和接收使用的是同一个缓冲区

int MPI_Sendrecv_replace(
    #指向包含消息的内存块的指针
    void* buf_p /*in/out*/,
    #指定要发送/接收的数据量
    int buf_size /*in*/,
    #数据类型MPI
    MPI_Datatype buf_type /*in*/,
    #指定要接收消息的进程号(可以与source相同也可以不相同,一般相同,代表向源发送,再向源接收)
    int dest /*in*/,
    #tag:0用于表示消息要打印的,为1表示消息用于计算的,要与发送的消息相匹配
    int send_tag /*in*/,
    #指定要接收消息的进程号(可以与dest相同也可以不相同,一般相同,代表向源发送,再向源接收)
    int source /*in*/,
    #tag:0用于表示消息要打印的,为1表示消息用于计算的,要与发送的消息相匹配
    int recv_tag /*in*/,
    #通信子
    MPI_Comm  commuicator /*in*/,
    #特殊MPI常量MPI_STATUS_IGNORE
    MPI_Status* status_p /*in*/)

%MPI_Status用法

在不知道以下信息的情况下接收消息
1)消息的数据量
2)消息的发送者
3)消息的标签
MPI结构

typedef struct MPI_Status {
    int count;
    int cancelled;
    int MPI_SOURCE;
    int MPI_TAG;
    int MPI_ERROR;
} MPI_Status;
#接收到的数据量不是存储在应用程序可以直接访问到的域中,但用户可以调用MPI_Get_count函数找回这个值
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>

const int MAX_STRING= 100;
int main()
{
     char  greeting[MAX_STRING];
     int comm_sz;
     int my_rank;
     int q;
     int buffer_size=0;
     MPI_Status status;

     MPI_Init(NULL,NULL);
     MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
     MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);

     if(my_rank != 0)
     {
        sprintf(greeting,"Greetings from process %d of %d!",my_rank,comm_sz);
        MPI_Send(greeting,strlen(greeting)+1,MPI_CHAR,0,0,MPI_COMM_WORLD);
     }
     else
     {
        printf("Greetings from process %d of %d !\n",my_rank,comm_sz);

        for(q=1;q<comm_sz;q++)
        {
            //对消息进行锁定测试(不接收消息) 
            /*
            int MPI_Probe(
                                #源进程序号 
                                int source, 
                                #消息标识
                                int tag, 
                                #通信子
                                MPI_Comm comm, 
                                #状态(返回的)
                                MPI_Status *status) 
            
            */
            MPI_Probe(q,0,MPI_COMM_WORLD,&status);
            MPI_Get_count(&status, MPI_CHAR, &buffer_size);
            MPI_Recv(greeting,buffer_size,MPI_CHAR,q,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
            printf("%s\n",greeting);
        }
     }
     MPI_Finalize();
    return 0;
}

集合通信和点对点通信多方面不同

  • 在通信子中 所有进程必须调用相同的集合通信函数
  • 每个进程传递给MPI集合函数必须是 “相容的”
  • 参数output_data_p只用在dest_process上。然而,所有进程仍需要传递一个与outputdata_p相对应的实际参数,即使它的值只是NULL
  • 点对点通信函数通过标签和通信子匹配。
MPI_Reduce

树形结构通信


PS:可用于N维度向量

int MPI_Reduce(
    #发送缓冲区地址
    void*  input_data_p /*in*/,
    #接收缓冲区地址 
    void*  output_data_p /*out*/,
    #发送缓冲区内元素数
    int  count /*in*/,
    #发送元素的数据类型
    MPI_Datatype datatype /*in*/,
    #规约操作
    MPI_Op  operator /*in*/,
    #存放结果的根进程号
    int  dest_process /*in*/,
    #通信子
    MPI_Comm  comm /*in*/)
运算符值 含义
MPI_MAX 求最大值
MPI_MIN 求最小值
MPI_SUM 求累加和
MPI_PROD 求累乘积
MPI_LAND 逻辑与
MPI_BAND 按位与
MPI_LOR 逻辑或
MPI_BOR 按位或
MPI_LXOR 逻辑异或
MPI_BXOR 按位异或
MPI_MAXLOC 求最大值和最小值所在的位置
MPI_MINLOC 求最小值和最大值所在的位置
MPI_Allreduce

蝶形结构通信

int MPI_Allreduce(
    #发送缓冲区地址 
    void*  input_data_p /*in*/,
    #接收缓冲区地址 
    void* output_data_p /*out*/,
    #发送缓冲区内元素数 
    int  count /*in*/,
    #发送元素的数据类型 
    MPI_Datatype  datatype /*in*/,
    #规约操作
    MPI_Op  operator /*in*/,
    #通信子
    MPI_Comm  comm /*in*/)
MPI_Bcast

树形结构广播

  • 阻塞式

引自:http://blog.csdn.net/sinat_22336563/article/details/70229243
int MPI_Bcast(
    #接收缓冲区(已装载)
    void*  data_p /*in/out*/,
    #缓冲区内最大项数
    int  count /*in*/,
    #项的数据类型 
    MPI_Datatype  datatype /*in*/,
    #根进程序号 
    int  source /*in*/,
    #通信子
    MPI_Comm  comm /*in*/)
MPI_Scatter

散射

引自:http://blog.csdn.net/sinat_22336563/article/details/70229243

int MPI_Scatter(
    #发送缓冲区
    void* send_buf_p /*in*/,
    #发送到每个进程的数据量(一般和recv_count相同)
    int  send_count /*in*/,
    #发送元素的数据类型
    MPI_Datatype  send_type /*in*/,
    #接收缓冲区(已装载)
    void* recv_buf_p /*out*/,
    #每次接收的元素数
    int   recv_count /*in*/,
    #接收元素的数据类型
    MPI_Datatype  recv_type /*in*/,
    #接收进程序号
    int  src_proc /*in*/,
    #通信子
    MPI_Comm  comm /*in*/)
%MPI_Scatterv

不等长散射

int MPI_Scatterv(
    #发送的内存地址
    void* sendbuf /*in*/,
    #发送sendcount[q]是发送q的sendtype类型的对象的数量
    int* sendcounts /*in*/,
    #偏移量的起始位置sendbuf+displacements[q]
    int* displacements /*in*/,
    #数据类型
    MPI_Datatype sendtype /*in*/,
    #接收的数据缓存块
    void* recvbuf /*out*/,
    #接收的个数
    int recvcount /*in*/,
    #发送根目录
    int root /*in*/,
    #通信子
    MPI_Comm comm /*in*/)
MPI_Gather

聚集

引自:http://blog.csdn.net/sinat_22336563/article/details/70229243

int MPI_Gather(
    #发送缓冲区
    void* send_buf_p /*in*/,
    #发送缓冲区内元素数
    int  send_count /*in*/,
    #发送元素的数据类型
    MPI_Datatype  send_type /*in*/,
    #接收缓冲区(已装载)
    void* recv_buf_p /*out*/,
    #从每个进程中接收到的数据量(一般和send_count相同)
    int   recv_count /*in*/,
    #接收元素的数据类型
    MPI_Datatype  recv_type /*in*/,
    #接收进程序号
    int  dest_proc /*in*/,
    #通信子
    MPI_Comm  comm /*in*/)
%MPI_Gatherv

不等长聚集

int MPI_Gatherv(
    #发送的内存地址
    void* sendbuf /*in*/,
    #发送对应内存的元素个数
    int sendcounts /*in*/,
    #数据类型
    MPI_Datatype sendtype /*in*/,
    #接收的数据缓存块
    void* recvbuf /*out*/,
    #接收recvcounts[q]是发送q的sendtype类型的对象的数量
    int* recvcounts /*in*/,
    #偏移量的起始位置recvbuf+displacements[q]
    int* displacements /*in*/,
    #接收数据的根目录
    int root /*in*/,
    #通信子
    MPI_Comm comm /*in*/)
MPI_Allgather

全局聚集

int MPI_Allgather(
    #发送缓冲区
    void* send_buf_p /*in*/,
    #发送缓冲区内元素数
    int  send_count /*in*/,
    #发送元素的数据类型
    MPI_Datatype  send_type /*in*/,
    #接收缓冲区(已装载)
    void* recv_buf_p /*out*/,
    #从每个进程中接收到的数据量(一般和send_count相同)
    int   recv_count /*in*/,
    #接收元素的数据类型
    MPI_Datatype  recv_type /*in*/,
    #通信子
    MPI_Comm  comm /*in*/)

MPI_派生数据类型

http://www.exuyi.com/course/MPI/content/chapter13/section1/part01/index1.htm

主要思想:如果发送数据的函数知道数据项的类型以及在内存中数据项集合的相对位置,就可以在数据项被发送出去之前数据项聚集起来。类似地,接收数据的函数可以在数据项被接收后数据分发到它们的内存中的正确的目标地址中
正式地,一个派生数据类型是由一系列的MPI基本数据类型和每个数据类型的偏移所组成的

变量 地址
a 24
b 40
n 48

以a为起点偏移量为0,b:40-24=16,n:48-24=24
{(MPI_DOUBLE,0),(MPI_DOUBLE,16),(MPI_INT,24)}

MPI_Get_address
int MPI_Get_address(
    #所指向的内存单元地址
    void* location_p /*in*/,
    #特殊类型MPI_Aint,长度足以表示系统地址
    MPI_Aint* address_p /*out*/)
Sample 2:
MPI_Aint a_addr,b_ddr,n_addr;;

MPI_Get_address(&a,&a_addr);
array_of_displacements[0]=0;
MPI_Get_address(&b,&b_addr);
array_of_displacements[1]=b_addr-a_addr;
MPI_Get_address(&n,&n_addr);
array_of_displacements[2]=n_addr-a_addr;
MPI_Type_create_struct

创建由不同基本数据类型的元素所组成的派生数据类型

int MPI_Type_create_struct(
    #数据类型中元素个数(例:double,double,int = 3)
    int  count /*in*/,
    #每个数据项的元素个数(可以是数组)(例:{1,1,1})
    int  array_of_blocklengths[] /*in*/,
    #每个数据项距离消息起始位置的偏移量(例:{0,16,24})
    MPI_Aint  array_of_displacements[] /*in*/,
    #每个数据项的类型(例:{MPI_DOUBLE,MPI_DOUBLE,MPI_INT})
    MPI_Datatype  array_of_types[] /*in*/,
    #自定义数据类型出口(例:MPI_Datatype input_mpi_t)
    MPI_Datatype*  new_type_p /*out*/)
MPI_Type_commit

允许MPI实现为了在通信函数内使用这一数据类型(例:input_mpi_t)

int MPI_Type_commit(
    #新构建的数据类型(例:MPI_Datatype input_mpi_t)
    MPI_Datatype* new_mpi_t_p /*in/out*/)
MPI_Type_free

当我们使用新的数据类型时,可以调用一个函数去释放额外的存储空间。

int MPI_Type_free(
    #已构建的数据类型(例:MPI_Datatype input_mpi_t)
    MPI_Datatype* old_mpi_t_p /*in/out*/)
Sample 3:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>

#define NELEM 5

typedef struct
{
        char pt;
        double pos[3];
        double orientation[3][3];
}Particle;
int main()
{
    int comm_sz;
    int my_rank;
    int source=0;
    int tag=1;
    int i,j,k;
    MPI_Status stat;

    Particle p[NELEM],particles[NELEM];
    MPI_Datatype particletype;
    #项目类型
    MPI_Datatype oldtype[3]={MPI_CHAR,MPI_DOUBLE,MPI_DOUBLE};
    #个数
    int blockcount[3]={1,3,9};
    #偏移
    MPI_Aint offset[3];

    MPI_Init(NULL,NULL);
    MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);
    MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);

    MPI_Address(&(particles[0].pt),&offset[0]);
    MPI_Address(particles[0].pos,&offset[1]);
    MPI_Address(particles[0].orientation,&offset[2]);

    for(i=2;i>=0;i--)
        offset[i]-=offset[0];

    MPI_Type_struct(3,blockcount,offset,oldtype,&particletype);
    MPI_Type_commit(&particletype);

    if(my_rank == 0)
    {
        for(i=0;i<NELEM;i++)
        {
                particles[i].pt = 'H';

                particles[i].pos[0] = 0.0;
                particles[i].pos[1] = 1.0;
                particles[i].pos[2] = 2.0;

                for(j=0;j<3;j++)
                    for(k=0;k<3;k++)
                        particles[i].orientation[j][k]=0.99;
        }

        for(i=1;i<comm_sz;i++)
        {
            MPI_Send(particles,NELEM,particletype,i,tag,MPI_COMM_WORLD);
        }
    }
    else
    {
            MPI_Recv(p,NELEM,particletype,source,tag,MPI_COMM_WORLD,&stat);

            printf("%d \n",p[2].pt);
            printf("%f %f %f \n",p[2].pMPI_Type_struct(5,blockcount,offset,oldtype,&particletype);os[0],p[2].pos[1],p[2].pos[2]);
            for(i=0;i<3;i++)
            {
                    for(j=0;j<3;j++)
                    {
                        printf("%f ",p[2].orientation[i][j]);
                    }
                    printf("\n");
            }
            printf("\n");
    }
    MPI_Finalize();
    return 0;
}

MPI_Type_create_resized

参考地址: http://blog.csdn.net/ccmpi/article/details/47733503

创建一个能在数据类型上缓冲的新属性类型

int MPI_Type_create_resized(
        #旧数据类型
        MPI_Datatype oldtype,  
        #下限
        MPI_Aint     lb,
        #宽度
        MPI_Aint     extent,
        #新类型
        MPI_Datatype *newtype)
Sample 9:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#include <mpi.h>

int my_rank;
int comm_sz;
MPI_Comm comm;

void Get_args(int argc, char* argv[], int* m_p, int* iters_p);
MPI_Datatype Build_cyclic_type(int n, int local_n);
void Print_loc_array(double x[], int n);

int main(int argc,char* argv[])
{
    int m,iters;
    int n,local_n,i;
    double* local_x;
    double* x;
    MPI_Datatype cyclic_mpi_t;
    double start,finish,my_elapsed,elapsed;

    MPI_Init(&argc, &argv);
    comm = MPI_COMM_WORLD;
    MPI_Comm_size(comm, &comm_sz);
    MPI_Comm_rank(comm, &my_rank);


    Get_args(argc, argv, &m, &iters);
    local_n = m ;
    n = m*comm_sz;
    local_x = malloc(local_n * sizeof(double));
    x = malloc(5*n*sizeof(double));

    for(i=0;i<local_n;i++)
            local_x[i] = my_rank+1;

    cyclic_mpi_t = Build_cyclic_type(n, local_n);

    MPI_Barrier(comm);
    start = MPI_Wtime();
    for(i=0;i<iters;i++)
    {
        MPI_Allgather(local_x,local_n,MPI_DOUBLE,x,local_n,MPI_DOUBLE,comm);
        if (my_rank == 0)
        {
            Print_loc_array(x, n);
            memset(x, '0', sizeof(double)*n);
        }
    }
    finish = MPI_Wtime();
    my_elapsed = finish - start;
    MPI_Reduce(&my_elapsed, &elapsed, 1, MPI_DOUBLE, MPI_MAX, 0, comm);

    if (my_rank == 0)
            printf("Average elapsed time for block Allgather = %e seconds\n", elapsed/iters);

        MPI_Barrier(comm);
        start = MPI_Wtime();
        for (i = 0; i < iters; i++)
        {
            MPI_Allgather(local_x, local_n, MPI_DOUBLE,x, 1, cyclic_mpi_t, comm);

            if (my_rank == 0)
            {
                    Print_loc_array(x, 5*n);
                    memset(x, '0', sizeof(double)*n);
            }
        }

        finish = MPI_Wtime();
        my_elapsed = finish-start;
        MPI_Reduce(&my_elapsed, &elapsed, 1, MPI_DOUBLE, MPI_MAX, 0, comm);

        if (my_rank == 0)
            printf("Average elapsed time for cyclic Allgather = %e seconds\n",elapsed/iters);

    MPI_Finalize();
    return 0;
}

void Get_args(int argc, char* argv[], int* m_p, int* iters_p)
{
    if(my_rank ==0)
    {
        *m_p = strtol(argv[1],NULL,10);
        *iters_p = strtol(argv[2],NULL,10);
    }

    MPI_Bcast(m_p, 1, MPI_INT, 0, comm);
    MPI_Bcast(iters_p, 1, MPI_INT, 0, comm);
}

MPI_Datatype Build_cyclic_type(int n, int local_n)
{
    MPI_Datatype temp_mpi_t, temp1_mpi_t;
    MPI_Aint lb, extent;

   MPI_Type_vector(local_n, 1, comm_sz, MPI_DOUBLE, &temp_mpi_t);
   MPI_Type_get_extent(MPI_DOUBLE, &lb, &extent);
   MPI_Type_create_resized(temp_mpi_t, lb,2*extent, &temp1_mpi_t);
   MPI_Type_commit(&temp1_mpi_t);
   return temp1_mpi_t;
}

void Print_loc_array(double x[], int n)
{
   int j;

   printf("x = ");
   for (j = 0; j < n; j++)
      printf("%2.0f ", x[j]);
   printf("\n");
   fflush(stdout);
} 

MPI 程序性能评估

时钟计时

MPI_Wtime()
#返回从过去某一个时刻开始所经过的秒数,墙上时钟
double MPI_Wtime(void)
Sample 4:
double start,finish;
...
start = MPI_Wtime();
...
finish = MPI_Wtime();
printf("%e \n",finish-start);
MPI_Barrier

能够确保同一通信子所有进程都完成调用该函数之前,没有进程能够提前返回。

                         #通信子
int MPI_Barrier(MPI_Comm comm /*int*/)
Sample 5:
double local_start,local_finish,local_elapsed,elapsed;
...
MPI_Barrier(comm);
local_start = MPI_Wtime();
...
local_finish = MPI_Wtime();
local_elapsed = local_finish - local_start;

MPI_Reduce(&local_elapsed,&elapsed,1,MPI_DOUBLE,MPI_MAX,0,comm);

if(my_rank == 0)
  printf("%e \n",elapsed);
MPI_Scan

计算前缀和,分配到各个节点

int MPI_Scan(
    #发送缓冲区地址
    void* sendbuf_p /*int*/,
    #接收缓冲区地址
    void* recvbuf_p /*out*/,
    #sendbuf_p和recvbuf_p都应该指向有count个datatype类型元素的数据块
    int count /*int*/,
    #发送元素的数据类型
    MPI_Datatype datatype /*int*/,
    #规约操作
    MPI_Op op /*int*/,
    #通信子
    MPI_Comm comm /*int*/)
Sample 6:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>

int main()
{
    int comm_sz;
    int my_rank;
    int source=0;
    int tag=1;
    double local_num;
    double  sum;

    MPI_Init(NULL,NULL);
    MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);
    MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);

    local_num = my_rank;
    printf("%d ----> %f \n",my_rank,local_num);

    MPI_Scan(&local_num,&sum,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);

    printf("%d ans----> %f\n",my_rank,sum);

    MPI_Finalize();
    return 0;
}
%MPI_Type_contiguous

从数组中收集邻接元素,然后创建派生数据类型

int MPI_Type_contiguous(
    #复制旧数据类型次数
    int count /*int*/,
    #原来数据类型
    MPI_Datatype old_mpi_t /*int*/,
    #新数据类型
    MPI_Datatype* new_mpi_t_p /*out*/)
取位:(MPI_INT,0),(MPI_INT,4),(MPI_INT,8),(MPI_INT,12)
Sample 7:
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

int main(void)
{

       int x[4];

       int i,j;
       int comm_sz;
       int my_rank;
       MPI_Datatype new_type;

       int index=0;
        for(i=0;i<4;i++)
        {
             x[i]=(++index);
             //y[i]=-1;    一旦初始化便会出错。原因未知
        }

        MPI_Init(NULL,NULL);
        MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
        MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);

        MPI_Type_contiguous(4,MPI_INT,&new_type);
        MPI_Type_commit(&new_type);

        if(my_rank == 0)
        {
                MPI_Send(x,1,new_type,1,0,MPI_COMM_WORLD);
        }
        else if(my_rank == 1)
        {
                int* y=malloc(4*sizeof(int));
                MPI_Recv(y,4,MPI_INT,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);

                for(i=0;i<4;i++)
                        printf("%d \n",y[i]);
        }
        MPI_Finalize();
        return 0;
}

%MPI_Type_vector

可以用来将数组中的数据块组合起来构建派生数据类型,这些块大小相同,在数组中的间隔是等距的。

int MPI_Type_vector(
    #块的个数
    int count /*in*/,
    #块的大小
    int blocklength /*in*/,
    #块与块的偏移量
    int stride /*in*/,
    #旧MPI数据类型
    MPI_Datatype old_mpi_t /*in*/,
    #新MPI数据类型
    MPI_Datatype* new_mpi_t_p /*out*/)
只需给予数组头指针,即可按当前偏量分配 取位:(MPI_DOUBLE,0),(MPI_DOUBLE,8),(MPI_DOUBLE,48),(MPI_DOUBLE,56),(MPI_DOUBLE,96),(MPI_DOUBLE,108)
Sample 8:
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

int main(void)
{

       int x[18];
       int y[6];

       int i,j;
       int comm_sz;
       int my_rank;
       MPI_Datatype new_type;

       int index=0;
        for(i=0;i<16;i++)
        {
             x[i]=(++index);
             //y[i]=-1;    一旦初始化便会出错。原因未知
        }

        MPI_Init(NULL,NULL);
        MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
        MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);

        MPI_Type_vector(3,2,6,MPI_INT,&new_type);
        MPI_Type_commit(&new_type);

        if(my_rank == 0)
        {
                MPI_Send(x,1,new_type,1,0,MPI_COMM_WORLD);
        }
        else if(my_rank == 1)
        {
                //int* y=malloc(6*sizeof(int));
                MPI_Recv(y,6,MPI_INT,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);

                for(i=0;i<6;i++)
                        printf("%d \n",y[i]);
        }
        MPI_Finalize();
        return 0;
}

%MPI_Type_indexed

建立取自任意数组元素的派生数据类型。

int MPI_Type_indexed(
    #数据类型中元素个数
    int count; /*in*/,
    #每个数据项的元素个数(可以是数组)
    int  array_of_blocklengths[] /*in*/,
    #每个数据项距离消息起始位置的偏移量
    int  array_of_displacements /*in*/,
    #旧数据类型
    MPI_Datatype old_mpi_t /*in*/,
    #自定义数据类型出口
    MPI_Datatype*  new_type_p /*out*/)
取位:(MPI_INT,0),(MPI_INT,4),(MPI_INT,8),(MPI_INT,12),(MPI_INT,20),(MPI_INT,24),(MPI_INT,28),(MPI_INT,40),(MPI_INT,44),(MPI_INT,60)
Sample 9:
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

int main(void)
{

       int x[16];


       int i,j;
       int comm_sz;
       int my_rank;
       MPI_Datatype new_type;

       int* array_of_block_lens;
       int* array_of_disps;

       array_of_block_lens = malloc(4*sizeof(int));
       array_of_disps = malloc(4*sizeof(int));

       int index=0;
        for(i=0;i<16;i++)
        {
             x[i]=(++index);
        }

        for( i=0;i<4;i++)
        {
            array_of_block_lens[i] = 4-i;
            array_of_disps[i]=i*(4+1);
        }

        MPI_Init(NULL,NULL);
        MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
        MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);

        MPI_Type_indexed(4,array_of_block_lens,array_of_disps,MPI_INT,&new_type);
        MPI_Type_commit(&new_type);

        if(my_rank == 0)
        {
                MPI_Send(x,1,new_type,1,0,MPI_COMM_WORLD);
        }
        else if(my_rank == 1)
        {
                int* y=malloc(10*sizeof(int));
                MPI_Recv(y,10,MPI_INT,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);

                for(i=0;i<10;i++)
                        printf("%d \n",y[i]);
        }
        MPI_Finalize();
        return 0;
}
%MPI_Pack

每次复制一块要发送的数据到用户提供的缓冲区,该缓冲区既可以接收数据或者发送数据。

int MPI_Pack(
    #放入存储区的内存地址
    void* in_buf /*in*/,
    #放入存储区的个数
    int in_buf_count /*in*/,
    #元素的数据类型
    MPI_Datatype datatype /*in*/,
    #放入的数据包
    void* pack_buf /*in*/,
    #数据包大小
    int pack_buf_sz /*in*/,
    #关键参数,当调用MPI_Pack时,该参数指向pack_buf中第一个可访问元素的位置
    int* position_p /*in/out*/,
    #通信子
    MPI_Comm comm /*int*/)
%MPI_Unpack

将缓冲区的数据解包
PS:除了进程0以外,其他进程都可以用MPI_Unpack来解包数据

int MPI_Unpack(
    #数据包内存地址
    void* pack_buf /*in*/,
    #数据包大小
    int pack_buf_sz /*in*/,
    #关键参数,当调用MPI_Unpack时,该参数指向pack_buf中第一个可访问元素的位置
    int* position_p /*in/out*/,
    #放入数据的内存地址
    void* out_buf /*in*/,
    #放入数据的内存大小
    int out_buf_count /*in*/,
    #元素的数据类型
    MPI_Datatype datatype /*in*/,
    #通信子
    MPI_Comm comm /*int*/)
Sample 10:
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

const int PACK_BUF_SZ=100;
int main(void)
{
       char  pack_buf[PACK_BUF_SZ];
       int position = 0;
       int i,j;
       int comm_sz;
       int my_rank;

        double a;
        double b;
        int n;

        MPI_Init(NULL,NULL);
        MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
        MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);

        if(my_rank == 0)
        {
            a=1.0;
            b=2.0;
            n=3;

            MPI_Pack(&a, 1, MPI_DOUBLE, pack_buf, PACK_BUF_SZ, &position, MPI_COMM_WORLD);
            MPI_Pack(&b, 1, MPI_DOUBLE, pack_buf, PACK_BUF_SZ, &position, MPI_COMM_WORLD);
            MPI_Pack(&n, 1, MPI_INT, pack_buf, PACK_BUF_SZ, &position, MPI_COMM_WORLD);
        }

        MPI_Bcast(pack_buf, PACK_BUF_SZ, MPI_PACKED, 0, MPI_COMM_WORLD);
        if (my_rank > 0)
        {
            MPI_Unpack(pack_buf, PACK_BUF_SZ, &position, &a, 1, MPI_DOUBLE,MPI_COMM_WORLD);
            MPI_Unpack(pack_buf, PACK_BUF_SZ, &position, &b, 1, MPI_DOUBLE,MPI_COMM_WORLD);
            MPI_Unpack(pack_buf, PACK_BUF_SZ, &position, &n, 1, MPI_INT,MPI_COMM_WORLD);
        }
        printf("Proc %d > a = %.2f, b = %.2f, n = %d\n", my_rank, a, b, n);

        MPI_Finalize();
        return 0;
}

%MPI_Iprobe

只测试是否有消息可用,不会真地接收消息。

int MPI_Iprobe(
    #来源进程,可以选用 MPI_ANY_SOURCE
    int source /*in*/,
    #tag:0用于表示消息要打印的,为1表示消息用于计算的。可以选用 MPI_ANY_TAG
    int tag /*in*/,
    #通信子
    MPI Comm comm /*in*/,
    #标记为tag的消息是否可用,可以则为true。否则为false
    int* msg_avail_p /*out*/,
    #特殊MPI常量,例如将status_p->MPI_SOURCE赋予接收到消息来源的线程号。
    MPI_Status* status_p /*out*/)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,460评论 25 707
  • 作者:邵正将PytLab,Python 中文社区专栏作者。主要从事科学计算与高性能计算领域的应用,主要语言为Pyt...
    Python中文社区阅读 4,824评论 1 31
  • 前言 Cobbler是一个自动化和简化系统安装的工具,通过使用网络引导来控制和启动安装。Cobbler的特性包括存...
    王奥OX阅读 1,650评论 0 6
  • 我的wiki地址最近研读了SDWebImage源码。细细品味了下SDWebImageDownloaderOpera...
    upworld阅读 1,192评论 5 4