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