欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MPI知识点总结

程序员文章站 2024-03-19 18:39:34
...

MPI知识点总结

  • 点对点
    • 阻塞式通信
      • MPI_Send
      • MPI_Recv
    • 非阻塞式通信
      • MPI_ISend
      • MPI_IRecv
      • MPI_Wait
  • 同步聚合
    • 栅栏同步
      • MPI_Barrier
  • 通信聚合
    • 广播
      • MPI_Bcast
    • 分散
      • MPI_Scatter
    • 收集
      • MPI_Gather
    • 全部收集
      • MPI_Allgather
  • 规约聚合
    • 规约操作
      • MPI_Reduce
      • MPI_Allreduce

 

几个重要函数

MPI_Scatter

MPI知识点总结

MPI_Scatter与MPI_Bcast非常相似,都是一对多的通信方式,不同的是后者的0号进程将相同的信息发送给所有的进程,而前者则是将一段array 的不同部分发送给所有的进程,其区别可以用下图概括:

MPI知识点总结

MPI_Scatter(
    void* send_data,//存储在0号进程的数据,array
    int send_count,//具体需要给每个进程发送的数据的个数
    //如果send_count为1,那么每个进程接收1个数据;如果为2,那么每个进程接收2个数据
    MPI_Datatype send_datatype,//发送数据的类型
    void* recv_data,//接收缓存,缓存 recv_count个数据
    int recv_count,
    MPI_Datatype recv_datatype,
    int root,//root进程的编号
    MPI_Comm communicator)

 

MPI_Gather

MPI知识点总结

MPI_Gather和MPI_scatter刚好相反,他的作用是从所有的进程中将每个进程的数据集中到根进程中,同样根据进程的编号对array元素排序,如图所示:

MPI知识点总结

MPI_Gather(
    void* send_data,
    int send_count,
    MPI_Datatype send_datatype,
    void* recv_data,
    int recv_count,//注意该参数表示的是从单个进程接收的数据个数,不是总数
    MPI_Datatype recv_datatype,
    int root,
    MPI_Comm communicator)

 

MPI_Allgather

MPI知识点总结

当数据分布在所有的进程中时,MPI_Allgather将所有的数据聚合到每个进程中。

MPI知识点总结

MPI知识点总结

MPI_Allgather(
    void* send_data,
    int send_count,
    MPI_Datatype send_datatype,
    void* recv_data,
    int recv_count,
    MPI_Datatype recv_datatype,
    MPI_Comm communicator)

 

MPI_Allgatherv

/*
 * 收集所有任务的数据并将组合数据传送到所有任务
 * int MPI_Allgatherv(const void * sendbuf,int sendcount,MPI_Datatype sendtype,
 *      void * recvbuf,const int * recvcounts,const int * displs,
 *      MPI_Datatype recvtype,MPI_Comm comm) 
 *
 * sendbuf:要发送内容的起始地址
 * sendcount:要发送的数量
 * sendtype:要发送数据的类型
 * recvbuf:接收数据要存放的单元的地址
 * recvcounts:这是一个整数数组,包含从每个进程要接收的数据量,比如{0, 1} 从0号进程接收0个,从1号进程接收1个 
 * displs:这是一个整数数组,包含存放从每个进程接收的数据相对于recvbuf的偏移地址
 * recvtype:要接收的数据类型
 * comm:通信集合
 */

MPI_Allagtherv解决MPI_Allagther要求每个进程处理任务量必须均等的问题,通过recvcounts数组指定每个进程接收任务的数量,通过displs数组指定每个进程发送的数据在接收缓冲区中的偏移量。

MPI知识点总结

 

MPI_Alltoall

int MPI_Alltoall(const void *sendbuf,    //发送缓冲区的起始地址
                int sendcount,           //要发送的数量
                 MPI_Datatype sendtype,  //发送的数据类型
                void *recvbuf,           //接收缓冲区的起始位置
                int recvcount,           //要接收的数量
                MPI_Datatype recvtype,   //要接收的类型
                MPI_Comm comm)           //通信子

函数原理示意图如下,分别演示了发送缓冲区数据量为1和2时的发送和接收情况:

MPI知识点总结

 

MPI_Alltoallv

有时候,我们当前进程往其他进程发送的数据不一样,个数也不一样,这个时候就需要用MPI_Alltoallv来解决。该函数用法类似于MPI_Allgather和MPI_Allgatherv的区别,即发送缓冲区的大小可以不相同(通过数组指定大小)。

int MPI_Alltoallv(const void *sendbuf,
                    const int *sendcounts,                                                            
                    const int *sdispls,
                     MPI_Datatype sendtype,
                    void *recvbuf,
                    const int *recvcounts,
                    const int *rdispls,
                    MPI_Datatype recvtype,
                    MPI_Comm comm)

相比MPI_Alltoall,这个方法有几个参数不一样:
int* sendcounts 和int *recvcounts。这两个参数你可以把它当做两个数组,数组中的元素代表往其他节点各发送(接收)多少数据。比如说,sendcounts[0]=3,sendcounts[1]=4,代表该节点要往0号节点发送3个sendtype的数据,往1号节点发送4个sendtype的数据。
多了两个参数,int *sdispls和int *rdispls,这两个可以看做是数组,数组中的每个元素代表了要发送(接收)的那块数据相对于缓冲区起始位置的位移量。

 

MPI_Reduce

MPI知识点总结

归约聚合类似于收集,但对收集的数据执行某种归约操作,例如计算总和,查找最大值或执行某些用户定义的操作。

规约函数 MPI_Reduce(),将通信子内各进程的同一个变量参与规约计算,并向指定的进程输出计算结果。

MPI_METHOD MPI_Reduce(
    _In_range_(!= , recvbuf) _In_opt_ const void* sendbuf,  // 指向输入数据的指针
    _When_(root != MPI_PROC_NULL, _Out_opt_) void* recvbuf, // 指向输出数据的指针,即计算结果存放的地方
    _In_range_(>= , 0) int count,                           // 数据尺寸,可以进行多个标量或多个向量的规约
    _In_ MPI_Datatype datatype,                             // 数据类型
    _In_ MPI_Op op,                                         // 规约操作类型
    _mpi_coll_rank_(root) int root,                         // 目标进程号,存放计算结果的进程
    _In_ MPI_Comm comm                                      // 通信子
);

demo:

{
    int size, rank, data, dataCollect;
    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    data = rank;// 参与计算的数据
    MPI_Reduce((void *)&data, (void *)&dataCollect, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);// 所有的进程都要调用,而不是只在目标进程中调用

    MPI_Finalize();
}

 

MPI_Allreduce

MPI知识点总结

规约并广播函数 MPI_Allreduce(),在计算规约的基础上,将计算结果分发到每一个进程中,相比于 MPI_Reduce(),只是少了一个 root 参数。除了简单的先规约再广播的方法,书中介绍了蝶形结构全局求和的方法。

少了一个root参数是因为该函数将计算的结果广播到每个进程中去,各个进程均更新数据。

_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Allreduce(
    _In_range_(!= , recvbuf) _In_opt_ const void* sendbuf,
    _Out_opt_ void* recvbuf,
    _In_range_(>= , 0) int count,
    _In_ MPI_Datatype datatype,
    _In_ MPI_Op op,
    _In_ MPI_Comm comm
);

demo:

{
    int size, rank, data, dataCollect;
    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    data = rank;
    MPI_Reduce((void *)&data, (void *)&dataCollect, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);// 所有的进程都要调用

    MPI_Finalize();
}