【转】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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM