mpi函数

论坛 期权论坛 编程之家     
选择匿名的用户   2021-5-21 12:19   11   0
头文件: mpi.h/mpif.h.
int MPI_Init(int *argc, char ***argv)
启动MPI环境,标志并行代码的开始.
并行代码之前,第一个mpi函数(除MPI_Initialize()外).
要求main必须带能运行,否则出错.
通信子(通信空间): MPI_COMM_WORLD:
一个通信空间是一个进程组和一个上下文的组合.上下文可看作为组的超级标签,用于区分不同的通信子.
在执行函数MPI_Init之后,一个MPI程序的所有进程形成一个缺省的组,这个组的通信子即被写作MPI_COMM_WORLD.
该参数是MPI通信操作函数中必不可少的参数,用于限定参加通信的进程的范围.

int MPI_Comm_size(MPI_Comm comm, int *size);

获得通信空间comm中规定的组包含的进程的数量.
指定一个communicator,也指定了一组共享该空间的进程, 这些进程组成该communicator的group.

int MPI_Comm_rank(MPI_Comm comm, int *rank);

得到本进程在通信空间中的rank值,即在组中的逻辑编号(从0开始).

int MPI_Finalize()
标志并行代码的结束,结束除主进程外其它进程.
之后串行代码仍可在主进程(rank = 0)上运行(如果必须).

int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);

IN buf 发送缓冲区的起始地址

IN count 要发送信息的元素个数

IN datatype 发送信息的数据类型

IN dest 目标进程的rank值

IN tag 消息标签

IN comm 通信子

int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status);

OUT buf 接收缓冲区的起始地址

IN count 要接收信息的元素个数

IN datatype 接收信息的数据类型

IN source 源进程的rank值

IN tag 消息标签

IN comm 通信子

OUT status status对象,包含实际接收到的消息的有关信息

MPI标识一条消息的信息包含四个域:
Source: 发送进程隐式确定,由进程的rank值唯一标识
Destination: Send函数参数确定
Tag: Send函数参数确定,用于识别不同的消息 (0,UB),UB:MPI_TAG_UB>=32767.
Communicator: 缺省MPI_COMM_WORLD
Group:有限/N,有序/Rank [0,1,2,…N-1]
Contex:Super_tag,用于标识该通讯空间.
当使用MPI_ANY_SOURCE或/和MPI_ANY_TAG接收消息时如何确定消息的来源source 和 tag值呢?
在C中,status.MPI_SOURCE, status.MPI_TAG.
在Fortran中, source=status(MPI_SOURCE), tag=status(MPI_TAG).
Status还可用于返回实际接收到消息的长度
int MPI_Get_count(MPI_Status status, MPI_Datatype datatype,int* count)
IN status 接收操作的返回值.
IN datatype 接收缓冲区中元素的数据类型.
OUT count 接收消息中的元素个数.
rank = MPI_PROC_NULL的进程称为空进程
使用空进程的通信不做任何操作.
向MPI_PROC_NULL发送的操作总是成功并立即返回.
从MPI_PROC_NULL接收的操作总是成功并立即返回,且接收缓冲区内容为随机数.
status
status.MPI_SOURCE = MPI_PROC_NULL
status.MPI_TAG = MPI_ANY_TAG
MPI_Get_count(&status,MPI_Datatype datatype, &count) =>count = 0
用户发送缓冲区的重用:
–非阻塞的发送:仅当调用了有关结束该发送的语句后才能重用发送缓冲区,否则将导致错误;对于接收方,与此相同,仅当确认该接收请求已完成后才能使用。所以对于非阻塞操作,要先调用等待MPI_Wait()或测试MPI_Test()函数来结束或判断该请求,然后再向缓冲区中写入新内容或读取新内容。
阻塞发送将发生阻塞,直到通讯完成.
非阻塞可将通讯交由后台处理,通信与计算可重叠.
发送语句的前缀由MPI_改为MPI_I, I:immediate:
–标准模式:MPI_Send(…)->MPI_Isend(…)
–Buffer模式:MPI_Bsend(…)->MPI_Ibsend(…)
int MPI_Isend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request)
–IN buf 发送缓冲区的起始地址
–IN count 发送缓冲区的大小(发送元素个数)
–IN datatype 发送缓冲区数据的数据类型
–IN dest 目的进程的秩
–IN tag 消息标签
–IN comm 通信空间/通信子
–OUT request 非阻塞通信完成对象(句柄)
int MPI_Irecv(void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request* request)
发送的完成: 代表发送缓冲区中的数据已送出,发送缓冲区可以重用。它并不代表数据已被接收方接收。数据有可能被缓冲;
接收的完成:代表数据已经写入接收缓冲区。接收者可访问接收缓冲区。
通过 MPI_Wait ()和 MPI_Test ()来判断通信是否已经完成;
int MPI_Wait(MPI_Request* request, MPI_Status * status);

当request标识的通信结束后,MPI_Wait()才返回。如果通信是非阻塞的,返回时request = MPI_REQUEST_NULL;函数调用是非本地的;

MPI_Probe()和MPI_Iprobe()函数探测接收消息的内容。用户根据探测到的消息内容决定如何接收这些消息,如根据消息大小分配缓冲区等。前者为阻塞方式,即只有探测到匹配的消息才返回;后者为非阻塞,即无论探测到与否均立即返回.
int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status* status)
int MPI_Iprobe(int source, int tag, MPI_Comm comm, int*flag, MPI_Status* status)
–IN source 数据源的rank,可以是MPI_ANY_SOURCE
–IN tag 数据标签,可以是MPI_ANY_TAG
–IN comm 通信空间/通信子
–OUT flag 布尔值,表示探测到与否(只用于非阻塞方式)
–OUT status status对象,包含探测到消息的内容
mpicc编译并连接用C语言编写的MPI程序
mpiCC编译并连接用C++编写的MPI程序
mpif77编译并连接用FORTRAN 77编写的MPI程序
mpif90编译并连接用Fortran 90编写的MPI程序

mpirun –np N <program>

其中:

N: 同时运行的进程数

<program>: 可执行MPI程序名

mpirun –p4pg <pgfile> <program>

<pgfile>为配置文件,其格式为:

<机器名> <进程数> <程序名>

<机器名> <进程数> <程序名>

<机器名> <进程数> <程序名>

例如: (注:第一行的0并不表示在node0上没有进程,这里的0特指在node0上启动MPI程序)

node0 0 /public0/dair/mpi/cpi

node1 1 /public0/dair/mpi/cpi

node2 1 /public0/dair/mpi/cpi

这种方式允许可执行程序由不同的名字和不同的路径

mpirun –machinefile <machinefile> -np <N> <program>

<machinefile>为配置文件,其格式为:

<机器名>

<机器名>

<机器名>

MPI程序的一般启动方式:

mpirun –np <number of processor> <program name and argument>

完整的MPI运行方式:

mpirun [mpirun_options] <program> [options…]

将原数据类型,按顺序进行多次复制
MPI_Type_contiguous(count, oldtype, newtype)
IN count 复制个数。
IN oldtype 旧数据类型。
举例:

float a[1000][1000]; MPI_Datatype C_R;

MPI_Type_contiguous(1000,MPI_FLOAT,&C_R);

MPI_SEND(&(a[0][0]),1,C_R, right,tag ,MPI_COMM_WORLD)

MPI_Vector()函数首先通过连续复制若干个旧数据类型形成一个“块”,然后通过等间隔地复制该块儿形成新的数据类型。块与块之间的空间时旧数据类型的倍数。

#include "mpi.h"

int MPI_Type_vector (

int count, /*数据块个数 (非负整数)*/

int blocklen, /*块中元素个数 (非负整数)*/

int stride, /*块间起始地址间隔 (非负整数)*/

MPI_Datatype old_type, /*原始数据类型(句柄)*/

MPI_Datatype *newtype /*派生数据类型指针*/

)

举例:

用MPI_Vector进行矩阵的行列置换

float A[10][10];

MPI_Datatype column_mpi_t;

MPI_Type_vector(10, 1, 10, MPI_FLOAT, &column_mpi_t);

MPI_Type_commit(&column_mpi_t);

if (my_rank == 0)

MPI_Send(&(A[0][0]), 1, column_mpi_t, 1, 0, MPI_COMM_WORLD);

else { /* my_rank = 1 */

MPI_Recv(&(A[0][0]), 10, MPI_FLOAT, 0, 0,

MPI_COMM_WORLD, &status);

int MPI_Type_indexed (

int count, /*数据块的个数,数据块间不连续*/

int blocklens[], /*每一数据块中元素的个数,为一个非负整型数组*/

int indices[], /*每一块数据在原始数据类型中的起始位置,整型数组*/

MPI_Datatype old_type, /*原始数据类型(名柄)*/

MPI_Datatype* newtype /*派生数据类型指针*/

)

举例:
MPI_Type_indexed应用示意(将A矩阵的上三角部分送到另一个处理器中的T矩阵的对应位置)

float A[n][n]; /* Complete Matrix */

float T[n][n]; /* Upper Triangle */

int displacements[n];

int block_lengths[n];

MPI_Datatype index_mpi_t;

for (i = 0; i < n; i++) {

block_lengths[i] = n-i;

displacements[i] = (n+1)*i;

}

MPI_Type_indexed(n, block_lengths, displacements,MPI_FLOAT, &index_mpi_t);

MPI_Type_commit(&index_mpi_t);

if (my_rank == 0)

MPI_Send(A, 1, index_mpi_t, 1, 0, MPI_COMM_WORLD);

else /* my_rank == 1 */

MPI_Recv(T, 1, index_mpi_t, 0, 0, MPI_COMM_WORLD, &status);

允许每个块不同数据类型的拷贝
MPI_Type_struct(count, array_of_blockths, array_of_displacements, array_of_types, newtype)
IN count 块的数量
IN array_of_blockths 每个块中所含元素数量
IN array_of_displacements 每块偏移字节数
IN array_of_types 块中元素类型
OUT newtype 新数据类型
举例:
MPI_Type_struct的例子

struct partstruct {char class; double d[6]; char b[7]};

struct partstruct particle[1000];

int i,dest,rank;

MPI_Datatype particletype,type[3]={MPI_CHAR, MPI_DOUBLE,MPI_CHAR}

int blocklen[3]={1,6,7};

MPI_Aint disp[3]={0,sizeof(double),7*sizeof(double)};

MPI_Type_struct(3,blocklen,disp,type,&particletype);

int MPI_Pack (

void *inbuf, /* 输入缓冲区起始地址*/

int incount, /* 输入数据项个数 */

MPI_Datatype datatype, /* 输入数据项的数据类型 */

void *outbuf, /* 输出缓冲区起始地址 */

int outcount, /* 输出缓冲区大小 */

int *position, /* 输出缓冲区当前位置 */

MPI_Comm comm /* 通信域 */

)

例:

packsize=0;

MPI_Pack(&a,1,MPI_INT,packbuf,100,&packsize,MPI_COMM_WORLD);

MPI_Pack(&b,1,MPI_DOUBLE, packbuf,100,&packsize,MPI_COMM_WORLD);

int MPI_Unpack (

void *inbuf, /* 输入缓冲区起始地址*/

int incount, /* 输入数据项大小*/

int *position, /* 缓冲区当前位置 */

void *outbuf, /* 输出缓冲区起始地址 */

int outcount, /* 输出缓冲区大小 */

MPI_Datatype datatype, /* 输出数据项的数据类型 */

MPI_Comm comm /* 通信域 */

)

例:

pos=0;

MPI_Unpack(packbuf,packsize,&pos,&a,1,MPI_INT,MPI_COMM_WROLD);

MPI_Unpack(packbuf,packsize,&pos,&b,1,MPI_FLOAT,MPI_COMM_WROLD);

提交:int MPI_Type_commit(MPI Datatype *datatype)
–将数据类型映射进行转换或“编译”
–一种数据类型变量可反复定义,连续提交
释放:int MPI_Type free(MPI_Datatype *datatype)
将数据类型设为MPI_DATATYPE_NULL

int MPI_Bcast (

void *buffer,/*发送/接收buf*/

int count, /*元素个数*/

MPI_Datatype datatype,

int root, /*指定根进程*/

MPI_Comm comm)

根进程既是发送缓冲区也是接收缓冲区

举例:

int p, myrank;

float buf;

MPI_Comm comm;

MPI_Init(&argc, &argv);

/*得进程编号*/

MPI_Comm_rank(comm, &my_rank);

/* 得进程总数 */

MPI_Comm_size(comm, &p);

if(myrank==0)

buf = 1.0;

MPI_Bcast(&buf,1,MPI_FLOAT,0, comm);

根进程接收其他进程来的消息(包括根进程),按每在进程在通信组中的编号依次联接在一下,存放在根进程的接收缓冲区中.

int MPI_Gather ( void *sendbuf, int sendcnt, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm ) 参数:

sendbuf 发送缓冲区起始位置
sendcount 发送元素个数
sendtype 发送数据类型
recvcount 接收元素个数(所有进程相同) (该参数仅对根进程有效)
recvtype 接收数据类型(仅在根进程中有效)
root 通过rank值指明接收进程
comm 通信空间
int MPI_Gatherv ( void *sendbuf, int sendcnt, MPI_Datatype sendtype, void *recvbuf, int *recvcnts, int *displs, MPI_Datatype recvtype, int root, MPI_Comm comm )
参数:
sendbuf 发送缓冲区的起始位置
sendcount 发送元素个数
sendtype 发送数据类型
recvcounts 整型数组(大小等于组的大小),用于指明从各进程要接收的元素的个数(仅对根进程有效)
displs 整型数组(大小等于组的大小). 其元素 i指明要接收元素存放位置相对于接收缓冲区起始位置的偏移量 (仅在根进程中有效)
recvtype 接收数据类型
root 通过rank值指明接收进程
comm 通信空间

/*得进程编号*/

MPI_Comm_rank(comm,&my_rank);

/* 得进程总数 */

MPI_Comm_size(comm, &p);

if(myrank==0)

buf=(float*)malloc(p*10*sizeof(float);/*开辟接收缓冲区*/

MPI_Gather(data,10,MPI_FLOAT,

buf,10,MPI_FlOAT,0,comm);

根进程中存储了p个消息,第i个消息将传给第i个进程.

int MPI_Scatter ( void *sendbuf, int sendcnt, MPI_Datatype sendtype, void *recvbuf, int recvcnt, MPI_Datatype recvtype, int root, MPI_Comm comm )

int p, myrank;

float data[10];

float* buf;

MPI_Comm comm;

MPI_Init(&argc, &argv);

/*得进程编号*/

MPI_Comm_rank(comm,&my_rank);

/* 得进程总数 */

MPI_Comm_size(comm, &p);

if(myrank==0)

buf = (float*)malloc(p*10*sizeof(float);/*开辟发送缓冲区*/

MPI_Scatter(buf,10,MPI_FLOAT,

data,10,MPI_FlOAT,0,comm);

对组中所有进程的发送缓冲区中的数据用OP参数指定的操作进行运算,并将结果送回到根进程的接收缓冲区中.

int MPI_Reduce ( void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm )

举例:

int p, myrank;

float data = 0.0;

float buf;

MPI_Comm comm;

MPI_Init(&argc, &argv);

/*得进程编号*/

MPI_Comm_rank(comm,&my_rank);

/*各进程对data进行不同的操作*/

data = data + myrank * 10;

/*将各进程中的data数相加并存入根进程的buf中 */

MPI_Reduce(&data,&buf,1,MPI_FLOAT,MPI_SUM,0,comm);

MPI环境管理
MPI起动与结束:
–MPI_Init();
–MPI_Initialized();测试是否已执行MPI_Init();
–MPI_Finalize();
MPI计时函数
–double MPI_Wtime();返回自过去某一时刻调用时的时间间隔,以秒为单位.
–double MPI_Wtick();返回用作硬件计时的两次脉冲间的间隔时间,以秒为单位.
组,上下文和通信空间管理.
通信域:描述进程间的通信关系,包括
–通信上下文:区别不同的通信域,一个上下文所发送的消息不能被另一个上下文所接收
–进程组:多个进程的有序集合
–虚拟拓扑:多个进程在逻辑上的排列关系,反映了进程间的通信模型
–属性:用户可通过自定义的属性,将任意信息附加到通信域上
MPI_GROUP_NULL
–无效进程组句柄
MPI_COMM_NULL
–无效通信域句柄
MPI_GROUP_EMPTY
–有效进程组句柄,包括元素个数为0
MPI_COMM_SELF
–有效通信域句柄,包括元素仅为当前进程
MPI_COMM_WORLD
–有效通信域句柄,包括元素为所有进程
不同的进程可以有不同的分工,可为之建立不同的通信域,这要借助进程组完成。
得到通信域对应的进程组:

MPI_COMM_GROUP(comm,group)

根据组创建新的通信域:

MPI_COMM_CREATE(comm,g,ncomm)

比较:

MPI_GROUP_COMPARE(g1,g2,result)

并:

MPI_GROUP_UNION(g1,g2,newg)

交:

MPI_GROUP_INTERSECTION(g1,g2,newg)

差:

MPI_GROUP_DIFFERENCE(g1,g2,newg)

所包含的进程形成新组:

MPI_GROUP_INCL(g,n,ranks,newg)

去除掉进程后形成新组:

MPI_GROUP_EXCL(g,n,ranks,newg)

得到组的大小

MPI_GROUP_SIZE(group,size)

得到当前进程在组中的编号

MPI_GROUP_RANK(group,rank)

比较

MPI_COMM_COMPARE(comm1,comm2,result)

复制

MPI_COMM_DUP(comm,newcomm)

释放

MPI_COMM_FREE(comm)

通信域的分裂MPI_COMM_SPLIT(comm,color,key,newcomm)
–根据color值的不同,形成不同的通信域
–根据key值的大小,决定新通信域中的编号顺序

在许多并行应用程序中,进程的线性排列不能充分地反映进程间的通信模型
进程经常被排列成二维或三维网格形式的拓扑模型,而且通常用一个图来描述逻辑进程排列
不同的进程拓扑结构,可使程序设计更为自然,也为在相近的物理拓扑上的高效实现提供支持。

int MPI_Topo_test(comm, status)

IN comm通信域

OUT status通信域comm的拓扑结构

MPI_CART_CREATE(comm_old, ndims, dims, periods, reorder, comm_cart)

IN comm_old 输入通信域

IN ndims 笛卡尔网格的维数

IN dims 大小为ndims的整数数组,定义每一维的进程数

IN periods 大小为ndims的逻辑数组,定义在一维上网格的周期性。

IN reorder 标识数是否可以重排序。

OUT comm_cart 带有新的笛卡尔拓扑的通信域。

MPI_DIMS_CREATE(nnodes, ndims,dims)

根据用户指定的总进程数nnodes和总维数ndims,得到每一维上的进程个数,放在dims中

MPI_CART_COORDS(comm, rank, maxdims, coords)

得到rank值对应的笛卡尔坐标

平移操作
MPI_CART_SHIFT(comm, direction, disp, rank_source, rank_dest)
创建图拓扑
MPI_GRAPH_CREATE(comm_old, nmodes, index, edges, reorder, comm_graph)
得到相邻进程数
MPI_GRAPH_NEIGHBORS_COUNT(comm, rank, nneighbors)
得到相邻进程标识
MPI_GRAPH_NEIGHBORS(comm, rank, maxneighbors, neighbors)

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP