【转】MPI入门

author: Menglong TAN; email: tanmenglong_at_gmail; twitter/weibo: @crackcell; source:http://blog.crackcell.com/posts/2013/07/15/mpi_quick_start.html.

1 前言

  不知道为啥,MPI的入门教程似乎很少,也不太明了。今天看了一些教程,整理一下入门需要知道的知识点。

2 开发环境设置

  环境:debian sid 安装开发环境:

$ sudo apt-get install openmpi-bin openmpi-doc libopenmpi-dev gcc g++

3 Learn by example

 

3.1 例子1:Hello world

#include <iostream>
#include <mpi/mpi.h>

using namespace std;

int main(int argv, char* argc[]){
    MPI_Init(&argv, &argc);
    cout << "hello world" << endl;
    MPI_Finalize();
    return 0;
}

编译:

$ mpicxx -o hello.exe hello.cpp

运行:

$ mpirun -np 10 ./hello.exe

  • -np 10 参数制定了运行了程序的10个拷贝

3.2 代码结构

   我们来看代码,MPI程序的结构一般是:

  1. 头文件、全局定义
  2. 初始化MPI环境:MPI_Init()
  3. 分布式代码
  4. 终止MPI环境:MPI_Finalize()
  5. 结束

3.3 一些基本的API

 

3.3.1 初始化环境:MPI_Init

#include <mpi.h>
int MPI_Init(int *argc, char ***argv)

3.3.2 是否初始化:MPI_Initialized

#include <mpi.h>
int MPI_Initialized(int *flag)

3.3.3 终止环境:MPI_Finalize

#include <mpi.h>
int MPI_Finalize()

3.3.4 获取进程数:MPI_Comm_size

获取一个communicator中的进程数

#include <mpi.h>
int MPI_Comm_size(MPI_Comm comm, int *size)

如果communicator是MPI_COMM_WORLD,那就是当前程序能用的所有进程数

3.3.5 获取当前进程id:MPI_Comm_rank

#include <mpi.h>
int MPI_Comm_rank(MPI_Comm comm, int *rank)

3.3.6 获取程序运行的主机名:MPI_Get_processor_name

#include <mpi.h>
int MPI_Get_processor_name(char *name, int *resultlen)

3.3.7 终止一个communicator的所有进程:MPI_Abort

#include <mpi.h>
int MPI_Abort(MPI_Comm comm, int errorcode)

3.4 例2:稍微复杂一点

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

int main(int argc, char *argv[]) {
    char hostname[MPI_MAX_PROCESSOR_NAME];
    int task_count;
    int rank;
    int len;
    int ret;

    ret = MPI_Init(&argc, &argv);
    if (MPI_SUCCESS != ret) {
        printf("start mpi fail\n");
        MPI_Abort(MPI_COMM_WORLD, ret);
    }

    MPI_Comm_size(MPI_COMM_WORLD, &task_count);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Get_processor_name(hostname, &len);

    printf("task_count = %d, my rank = %d on %s\n", task_count, rank, hostname);

    MPI_Finalize();

    return 0;
}

运行一下:

$ mpirun -np 3 ./hello3.exe task_count = 3, my rank = 0 on crackcell-vm0 task_count = 3, my rank = 1 on crackcell-vm0 task_count = 3, my rank = 2 on crackcell-vm0

3.5 基本通信API

  • MPI提供了消息的缓存机制
  • 消息可以以阻塞或非阻塞的方式发送
  • 顺序性:MPI保证接收者收到消息的顺序和发送者的发送顺序一致
  • 公平性:MPI不保证调度公平性,程序员自己去防止进程饥饿

3.5.1 消息数据类型

    为了可移植性,MPI定义了自己的消息数据类型,具体参考1

3.5.2 点对点通信API

  • 阻塞发送:MPI_Send
    int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest,
                 int tag, MPI_Comm comm)
    
  • 非阻塞发送:MPI_Isend
    int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest,
                 int tag, MPI_Comm comm)
    
  • 阻塞接收:MPI_Recv
    int MPI_Recv(void *buf, int count, MPI_Datatype datatype,
                 int source, int tag, MPI_Comm comm, MPI_Status *status)
    
  • 非阻塞接收:MPI_Irecv
    int MPI_Irecv(void *buf, int count, MPI_Datatype datatype,
                  int source, int tag, MPI_Comm comm, MPI_Request *request)
    

3.6 例3:阻塞的消息传递

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

int main(int argc, char *argv[]) {
    int task_count;
    int rank;
    int dest;
    int src;
    int count;
    int tag = 1;

    char in_msg;
    char out_msg = 'x';

    MPI_Status status;

    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &task_count);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    if (0 == rank) {
        dest = 1;
        src = 1;
        // 向1发送一个字符,然后等待返回
        MPI_Send(&out_msg, 1, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
        MPI_Recv(&in_msg, 1, MPI_CHAR, src, tag, MPI_COMM_WORLD, &status);
    } else if (1 == rank) {
        dest = 0;
        src = 0;
        // 向0发送一个字符,然后等待返回
        MPI_Recv(&in_msg, 1, MPI_CHAR, src, tag, MPI_COMM_WORLD, &status);
        MPI_Send(&out_msg, 1, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
    }

    MPI_Get_count(&status, MPI_CHAR, &count);
    printf("task %d: recv %d char(s) from task %d with tag %d\n",
           rank, count, status.MPI_SOURCE, status.MPI_TAG);

    MPI_Finalize();

    return 0;
}

3.7 协同通信API

  • 协同通信必须涉及同一个communicator中的所有进程
  • 协同通信操作的类型
    1. 同步操作:进程等待同组的其它成员到达某一同步点
    2. 数据移动操作:broadcast、scatter/gather操作
    3. 协同计算:某个成员收集其它成员的数据,然后执行某个操作

3.7.1 阻塞直到同组其它任务完成:MPI_Barrier

#include <mpi.h>
int MPI_Barrier(MPI_Comm comm)

3.7.2 Broadcast消息:MPI_Bcast

#include <mpi.h>
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype,
              int root, MPI_Comm comm)

3.7.3 散播消息:MPI_Scatter

#include <mpi.h>
int MPI_Scatter(void *sendbuf, int sendcount, MPI_Datatype sendtype,
                void *recvbuf, int recvcount, MPI_Datatype recvtype, int root,
                MPI_Comm comm)

3.7.4 收集消息:MPI_Gather

#include <mpi.h>
int MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype,
               void *recvbuf, int recvcount, MPI_Datatype recvtype, int root,
               MPI_Comm comm)

更多API参考2

3.8 组和通信器

  • 一堆有序的进程组成一个group,每个进程有一个唯一的整数标识
  • 一个communicator组织起了一堆需要相互之间通信的进程。MPI_COMM_WORLD包含了所有进程

group用来组织一组进程,communicator用来关联他们之前的通信关系。

Date: Mon Jul 15 11:55:20 2013

Author: Tan Menglong

Org version 7.9.3f with Emacs version 24

Validate XHTML 1.0

posted @ 2015-03-14 16:00  gkwang  阅读(2563)  评论(0编辑  收藏  举报