Microsoft PowerPoint - Tongji_MPI编程初步

Similar documents
Parallel Programming with MPI

PowerPoint 演示文稿

第7章-并行计算.ppt

消息传递并行编程环境MPI.doc

模板

PowerPoint 演示文稿

Parallel Programming with MPI

大綱介紹 MPI 標準介紹 MPI 的主要目標 Compiler & Run 平行程式 MPICH 程式基本架構 點對點通訊 函數介紹 集體通訊 函數介紹

MPI实验.doc

Slide 1

mpi

Trilinos 简介 Trilinos 简介 卢朓 Trilinos 简介 卢朓 Trilinos 简介 Trilinos 简介 Trilinos 的安装和使用 Trilinos 简介 Trilinos 简介 Trilinos 的安装和使用 Trilinos 简介 Trilinos Epetra

C++ 程式設計

C 1

C/C++程序设计 - 字符串与格式化输入/输出

C/C++ - 文件IO

chap07.key

1 Project New Project 1 2 Windows 1 3 N C test Windows uv2 KEIL uvision2 1 2 New Project Ateml AT89C AT89C51 3 KEIL Demo C C File

mannal

C/C++ 语言 - 循环

目录 第一章 MPI 简介 消息传递编程的相关概念 分布式内存 消息传输 进程 消息传递库 发送 / 接收 同步 / 异步 阻塞

C/C++语言 - C/C++数据

FY.DOC

untitled

CC213

untitled

C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 5 月 3 日 1

51 C 51 isp 10 C PCB C C C C KEIL

第3章.doc

Microsoft PowerPoint - os_4.ppt

C6_ppt.PDF

mpic_2002

C

c_cpp

Microsoft PowerPoint - 4. 数组和字符串Arrays and Strings.ppt [兼容模式]

, 7, Windows,,,, : ,,,, ;,, ( CIP) /,,. : ;, ( 21 ) ISBN : -. TP CIP ( 2005) 1

1 LINUX IDE Emacs gcc gdb Emacs + gcc + gdb IDE Emacs IDE C Emacs Emacs IDE ICE Integrated Computing Environment Emacs Unix Linux Emacs Emacs Emacs Un


mvc

Chapter #

3.1 num = 3 ch = 'C' 2

Microsoft PowerPoint - CEM-07-Parallel.pptx

C 1 # include <stdio.h> 2 int main ( void ) { 4 int cases, i; 5 long long a, b; 6 scanf ("%d", & cases ); 7 for (i = 0;i < cases ;i ++) 8 { 9

untitled

C/C++ - 数组与指针

ebook8-30

untitled

epub 33-8

int *p int a 0x00C7 0x00C7 0x00C int I[2], *pi = &I[0]; pi++; char C[2], *pc = &C[0]; pc++; float F[2], *pf = &F[0]; pf++;

C语言的应用.PDF

新・明解C言語入門編『索引』

呼 喊 選 集 2 3 天 國 大 英 雄 基 督 徒 因 此 在 基 督 快 將 再 臨 的 前 夕, 思 想 施 洗 約 翰 的 道 路, 對 我 們 來 說, 是 具 有 時 代 意 義 的 施 洗 約 翰 其 人 工 作 需 要 人, 需 要 合 用 的 人 在 神 的 國 度 中, 祂 所

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

6 C51 ANSI C Turbo C C51 Turbo C C51 C51 C51 C51 C51 C51 C51 C51 C C C51 C51 ANSI C MCS-51 C51 ANSI C C C51 bit Byte bit sbit

NOWOER.OM m/n m/=n m/n m%=n m%n m%=n m%n m/=n 4. enum string x1, x2, x3=10, x4, x5, x; 函数外部问 x 等于什么? 随机值 5. unsigned char *p1; unsigned long *p

CC213

C C

Microsoft PowerPoint - multicore curriculum of sspku.ppt

, 即 使 是 在 昏 暗 的 灯 光 下, 她 仍 然 可 以 那 么 耀 眼 我 没 有 地 方 去, 你 会 带 着 我 么 杜 晗 像 是 在 嘲 笑 一 般, 嘴 角 的 一 抹 冷 笑 有 着 不 适 合 这 个 年 龄 的 冷 酷 和 无 情, 看 着 江 华 的 眼 神 毫 无 温

Java

《C语言程序设计》第2版教材习题参考答案

Microsoft Word - FPKLSC_21.docx

Microsoft Word - 实用案例.doc

《C语言程序设计》教材习题参考答案

<4D F736F F D20482E323634B5F8B054BD73BD58A4A7A5ADA6E6A4C6ACE3A8732E646F63>

《嵌入式系统设计》教学大纲

C PICC C++ C++ C C #include<pic.h> C static volatile unsigned char 0x01; static volatile unsigned char 0x02; static volatile unsigned cha

Generated by Unregistered Batch DOC TO PDF Converter , please register! 浙江大学 C 程序设计及实验 试题卷 学年春季学期考试时间 : 2003 年 6 月 20 日上午 8:3

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例

BOOL EnumWindows(WNDENUMPROC lparam); lpenumfunc, LPARAM (Native Interface) PowerBuilder PowerBuilder PBNI 2

Open topic Bellman-Ford算法与负环

科学计算的语言-FORTRAN95

PowerPoint 演示文稿

OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢 学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料

nooog

第一章 概论

Transcription:

并行编程初步 张丹丹 上海超级计算中心 2011-3-4

提纲 引言 认识 MPI 编程 MPI 编程简介 实例

并行计算机体系架构 共享存储 (Shared Memory)

并行计算机体系架构 分布式存储 (Distributed Memory)

混合架构 (Hybrid) 并行计算机体系架构

并行编程模型 数据并行模型 相同的操作同时作用于不同的数据 共享变量模型 用共享变量实现并行进程间的通信 消息传递模型 在消息传递模型中, 驻留在不同节点上的进程可以通过网络传递消息相互通信, 实现进程之间的信息交换 协调步伐 控制执行等

并行编程模型 特征数据并行共享变量消息传递 典型代表 HPF OpenMP MPI,PVM 可移植性 SMP,DSM, MPP SMP,DSM 所有流行并行 计算机 并行粒度进程级细粒度线程级细粒度进程级大粒度 并行操作方式松散同步异步异步 数据存储模式共享存储共享存储分布式存储 数据分配方式半隐式隐式显示 学习入门难度偏易容易较难 可扩展性一般较差好

什么是 MPI? Massage Passing Interface: 是消息传递函数库 的标准规范, 由 MPI 论坛开发, 支持 Fortran 和 C/C++, 一种新的库描述, 不是一种语言 共有上百个函数调用接口, 在 Fortran 和 C/C++ 语言中 可以直接对这些函数进行调用 是一种标准或规范, 而不是特指某一个对它的具体实现 MPI 是一种消息传递编程模型, 并成为这种编程模型的 代表和事实上的标准

为什么要使用 MPI? 高可移植性 MPI 已在 PC 机 MS Windows 以及所有主要的 Unix 工作站上和所有主流的并行机上得到实现 使用 MPI 作消息传递的 C/C++ 或 Fortran 并行程序 可不加改变地在上述平台实现 没有更好的选择

常用的 MPI 版本 MPICH 是 MPI 最流行的非专利实现, 由 Argonne 国家实验室和密西西比州立大学联合开发, 具有更好的可移植性 当前最新版本有 MPICH 127p1 和 MPICH2 132p1 OpenMPI LAMMPI 的下一代 MPI 实现 当前最新版本 143 更多的商业版本 MPI HP-MPI,MS-MPI, 所有的版本遵循 MPI 标准,MPI 程序可以不加修改的运行

提纲 引言 认识 MPI 编程 MPI 编程简介 实例

从简单入手 下面我们首先分别以 C 语言的形式给出一个最简单的 MPI 并行程序 helloc 该程序在终端打印出 Hello World! 字样

Helloc (C 语言 ) #include <stdioh> #include "mpih main( int argc, char *argv[] ) { MPI_ Init( &argc, &argv ); printf( Hello World!\n"); MPI_Finalize(); }

MPI 程序的编译和运行 mpicc O2 o hello helloc 生成 hello 的可执行代码 mpirun np 4 hello 4,, 指定 np 的值, 表示进程数, 由用户指定 hello,, 要运行的 MPI 并行程序

Hello 是如何被执行的? SPMD: Single Program Multiple Data(MIMD) #include <stdioh> #include "mpih main(int argc,char *argv[]) { MPI_Init(&argc, &argv); printf( Hello World!\n"); MPI_Finalize(); } #include <stdioh> #include #include "mpih <stdioh> #include #include "mpih <stdioh> main(int #include #include argc,char "mpih <stdioh> *argv[]) { main(int #include argc,char "mpih *argv[]) MPI_Init(&argc, { main(int argc,char &argv); *argv[]) printf( Hello MPI_Init(&argc, { main(int World!\n"); argc,char &argv); *argv[]) MPI_Finalize(); printf( Hello MPI_Init(&argc, { Init(&argc World!\n"); &argv); } MPI_Finalize(); printf( Hello MPI_Init(&argc, World!\n"); &argv); } MPI_Finalize(); printf( Hello World!\n"); } MPI_Finalize(); } Hello World! Hello World! Hello World! Hello World!

MPI 程序运行模式

提纲 引言 认识 MPI 编程 MPI 编程简介 实例

MPI 初始化 - MPI_INIT int MPI_Init(int *argc, char **argv) MPI_INIT(IERROR) INIT(IERROR) MPI_INIT 是 MPI 程序的第一个调用, 完成 MPI 程序的所有初始化 工作 所有的 MPI 程序的第一条可执行语句都是这条语句 启动 MPI 环境, 标志并行代码的开始 并行代码之前, 第一个 mpi 函数 ( 除 MPI_Initialize 外 ) 要求 main 必须带参数运行 否则出错

MPI 结束 - MPI_FINALIZE int MPI_Finalize(void) MPI_ Finalize(IERROR) MPI_INIT 是 MPI 程序的最后一个调用, 它结束 MPI 程序的运 行, 它是 MPI 程序的最后一条可执行语句, 否则程序的运行结果是不可预知的 标志并行代码的结束, 结束除主进程外其它进程 之后串行代码仍可在主进程 (rank = 0) 上运行 ( 如果必须 )

C 和 Fortran 中 MPI 函数约定 C 必须包含 mpih MPI 函数返回出错代码或成功代码 MPI_SUCCESS MPI- 前缀, 且只有 MPI 以及 MPI_ 标志后的第一个字母大写, 其余小写, 例 :MPI_Init Fortran 必须包含 mpifh 通过子函数形式调用 MPI,, 函数最后一个值为返回值 MPI- 前缀, 且函数名全部大写, 例 :MPI_INIT INIT

开始写 MPI 程序 写 MPI 程序时, 我们常需要知道以下两个问题的答案 : 任务由多少进程来进行并行计算? 我是哪一个进程?

开始写 MPI 程序 MPI 提供了下列函数来回答这些问题 : 用 MPI_Comm_size 获得进程个数 p int MPI_Comm_size(MPI_Comm comm, int *size) 用 MPI_Comm_rank 获得进程的一个叫 rank 的值, 该 rank 值为 0 到 p-1 间的整数, 相当于进程的 ID int MPI_Comm_rank(MPI_Comm comm, int *rank)

更新的 Hello World(C 语言 ) #include <stdioh> #include "mpih main( int argc, char *argv[] ) { int myid,numprocs; int namelen; char processor_name[mpi_max_processor_name]; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD,&myid); _ MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Get_processor_name(processor_name,&namelen); fprintf(stderr, "Hello World!Process %d of %d on %s\n",myid,numprocs,processor_name); } MPI_Finalize();

更新后程序的运行结果 mpirun -np 4 /hellompi Hello World!Process 0 of 4 on blade01ssc Hello World!Process 1 of 4 on blade01ssc Hello World!Process 2 of 4 on blade01ssc Hello World!Process 3 of 4 on blade01ssc

问题 有 p 个进程, 除 0 号进程以外的所有进程都向 0 号进程发送消息 hello,0 号进程接收来自其他各个进程的问候

有消息传递 greetings(c 语言 ) #include <stdioh> #include "mpih" void main(int argc, char *argv[]) { int myid,numprocs,source; source; MPI_Status status; char message[100]; MPI_Init(&argc,&argv); MPI_Comm_rank(MPI_COMM_WORLD,&myid); MPI_Comm_size(MPI_COMM_WORLD,&numprocs);

有消息传递 greetings(c 语言 ) if (myid!= 0) { sprintf(message, "Hello! From process %d,myid); MPI_Send(message,strlen(message)+1,MPI_CHAR,0,99,MPI_COMM_WORLD); } else {/* myid == 0 */ for (source = 1; source < numprocs; source++) { MPI_Recv(message, 100, MPI_CHAR, source, 99,MPI_COMM_WORLD, &status); printf("%s\n",message); } } MPI_Finalize(); } /* end main */

Greeting 执行过程 进程 0 rank=0 进程 1 rank=1 进程 2 rank=2 进程 3 rank=3 Recv() Send() Send() Send()

解剖 greeting 程序 头文件 :mpih/mpifh int MPI_Init(int Init(int *argc, char ***argv) 通信组 / 通信子 : MPI_COMM_WORLD 一个通信组是一个进程组的集合 所有参与并行计算的进程可以组合为一个或多个通信组 执行 MPI_Init 后, 一个 MPI 程序的所有进程形成一个缺省的组, 这个组被写作 MPI_COMM_WORLD 该参数是 MPI 通信操作函数中必不可少的参数, 用于限定参加通信的进程的范围

解剖 greeting 程序 int MPI _ Comm _ size (MPI _ Comm comm, int *size) 获得通信组 comm 中包含的进程数 int MPI_Comm_rank (MPI_Comm comm, int *rank) 得到本进程在通信组中的 rank 值, 即在组中的逻辑编号 ( 从 0 开始 ) int MPI_Finalize()

消息传送 MPI_Send(A, 10, MPI_DOUBLE, 1, 99, MPI_COMM_WORLD); MPI_Recv(B, 20, MPI_DOUBLE, 0, 99, MPI_COMM_WORLD, &status); 数据传送 + 同步操作 需要发送方和接受方合作完成

最基本的 MPI 程序 MPI 函数的总数虽然庞大, 但根据实际编写 MPI 的经验, 常用的 MPI 调用的个数确实有限 下面是 6 个最基本也是最常用的 MPI 函数 MPI_Init( ); Init( MPI_Comm_size( ) MPI_Comm_rank( ) MPI_Send( ) MPI_Recv( ) MPI_Finalize()

MPI 程序设计流程

点对点通信 (P to P) 单个进程对单个进程的通信, 重要且复杂 术语 Blocking( 阻塞 ) : 一个函数须等待操作完成才返回, 返回后用户可以重新使用所占用的资源 Non-blocking( 非阻塞 ): 一个函数不必等待操作完成便可返回, 但这并不意味着所占用的资源可被重用 Local( 本地 ): 不通信 Non-local( 非本地 ): 通信

阻塞发送 (Blocking Send) int MPI_Send(void* buf, int count, MPI_Datatype datatype,int t t dest, int tag, MPI_Comm comm); IN buf 发送缓冲区的起始地址 IN count 要发送信息的元素个数 IN datatype IN dest IN tag 消息标签 IN comm 发送信息的数据类型 目标进程的 rank 值 通信组 将发送缓冲区中的 count 个 datatype 数据类型的数据发送到目的进程

阻塞接受 (Blocking Receive) int MPI_Recv(void* buf, int count, MPI_Datatype datatype,int t t source, int tag, MPI_Commcomm, MPI_Status *status); OUT buf 接收缓冲区的起始地址 IN count 最多可接收的数据的个数 ( 整型 ) IN datatype 接收数据的数据类型数据类型 IN source 接收数据的来源 IN tag 消息标签 IN comm 本进程和发送进程所在的通信域 OUT status status 对象, 包含实际接收到的消息的有关信息

MPI 数据传送 数据类型相匹配 Send 和 recv 匹配, 避免死锁 消息标签匹配

消息信封 (Message Envelope) 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] 1] Contex:Super_tag,, 用于标识该通讯空间

为什么使用消息标签 (Tag)? 为了说明为什么要用标签, 我们 未使用标签 先来看右面一段没有使用标签 Process P: Process Q: 的代码 : send(a,32,q) recv(x, 32, P) 这段代码打算传送 A 的前 32 send(b,16,q) recv(y, 16, P) 个字节进入 X, 传送 B 的前 16 个字 节进入 Y 但是, 如果消息 B 尽管后发送但先到达进程 Q, 就会被 使用了标签 第一个 recv() 接收在 X 中 Process P: Process Q: 使用标签可以避免这个错误 send(a,32,q,tag1) send(b,16,q,tag2) recv (X, 32, P, tag1) recv (Y, 16, P, tag2) 使用标志可将本次发送的消息与本进程向同一目的进程发送的其他消息区别开来

Process P: send (request1,32, Q) Process R: send (request2, 32, Q) 在消息传递中使用标签 Process Q: while (true) { recv (received_request, 32, Any_Process); process received_request; } Process P: send(request1, 32, Q, tag1) Process R: send(request2, 32, Q, tag2) Process Q: while (true){ recv(received_request, request 32, Any_Process, Any_Tag, Status); if (StatusTag==tag1) process received_request in one way; if (StatusTag==tag2) process received_request in another way; } 使用标签的另一个原因是可以简化对下列情形的处理 : 假定有两个客户进程 P 和 R, 每个发送一个服务请求消息给服务进程 Q

消息匹配 接收 buffer 必须至少可以容纳 count 个由 datatype 参数指明类型的数据 如果接收 buf 太小, 将导致溢出 出错 消息匹配 参数匹配 dest,tag,comm/ source,tag,comm Source == MPI_ANY_SOURCE: : 接收任意处理器来的数据 ( 任意消息来源 ) Tag == MPI_ANY_TAG: : 匹配任意 tag 值的消息 ( 任意 tag 消息 ) 在阻塞式消息传送中不允许 Source==Dest, 否则会导致死锁 消息传送被限制在同一个 communicator 在 send 函数中必须指定唯一的接收者

status 参数 当使用 MPI_ANY_SOURCE 或 / 和 MPI_ANY_TAG 接收消息时 如何确定消息的来源 source 和 tag 值? 在 C 中,statusMPI_SOURCE, statusmpi_tag 在 Fortran 中, source=status(mpi_source),tag=status(mpi_tag) Status 还可用于返回实际接收到消息的长度 int MPI_Get_count(MPI_Status MPI_Status status,mpi_datatype datatype,int* count) IN status 接收操作的返回值 IN datatype 接收缓冲区中元素的数据类型 OUT count 接收消息中的元素个数

分析 greetings 程序 #include <stdioh stdioh> #include "mpih main(intint argc, char* argv[]) { int numprocs; /* 进程数, 该变量为各处理器中的同名变量, 存储是分布的 */ int myid; /* 我的进程 ID, 存储也是分布的 */ MPI_Status status; /* 消息接收状态变量, 存储也是分布的 */ char message[100]; /* 消息 buffer, 存储也是分布的 */ /* 初始化 MPI*/ MPI_Init(& (&argc, &argv argv); /* 该函数被各进程各调用一次, 得到自己的进程 rank 值 */ MPI_Comm_rank(MPI_COMM_WORLD, &myid); /* 该函数被各进程各调用一次, 得到进程数 */ MPI_Comm_size(MPI_COMM_WORLD, &numprocs numprocs);

分析 greetings 程序 if (myid!= 0) { /* 建立消息 */ sprintf(message, "Hello!"); /* 发送长度取 strlen(message)+1, 使 \0 也一同发送出去 */ MPI_Send(message,strlen(message)+1, MPI_CHAR, 0,99,MPI_COMM_WORLD); } else { /* my_rank == 0 */ for (source = 1; source < numprocs; source++) { MPI_Recv(message, 100, MPI_CHAR, source, 99, MPI_COMM_WORLD,&status); printf( %s\n", message); } } /* 关闭 MPI, 标志并行代码段的结束 */ MPI_Finalize(); } /* End main */

Greetings 执行过程 假设进程数为 3 进程 0 rank=0 进程 1 rank=1 进程 2 rank=2 Recv()? Send() Send() 问题 : 进程 1 和进程 2 谁先向进程 0 发送消息? 执行结果如何?

分析 greetings 程序 source=0; //char buffer[10]; if (myid!= 0) { sprintf(message, "Hello"); MPI_Send(message,strlen(message)+1,MPI_CHAR,0,99,MPI_COMM_WORLD); } else {/* myid == 0 */ do { MPI_Recv(message, 100, MPI_CHAR, MPI_ANY_SOURCE, 99,MPI_COMM_WORLD, &status); source++; printf("%s from process %d \n",message, n",message,statusmpi_source statusmpi_source); }while(source<numprocs-1); } MPI_Finalize();

非阻塞发送与接收 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) 2011 年 3 月 MPI 并行程序设计 47/217

阻塞通信与非阻塞通信

MPI 数据类型 if (my_rank!= 0) { /* 建立消息 */ sprintf(message, Hello! ); /* 发送长度取 strlen(message)+1, 使 \0 也一同发送出去 */ MPI_Send(message,strlen(message)+1, MPI_CHAR, 0,99,MPI_COMM_WORLD); } else { /* my_rank == 0 */ for (source = 1; source < p; source++) { MPI_Recv(message, 100, MPI_CHAR, source, 99, MPI_COMM_WORLD,&status); printf( %s\n", message); } } /* 关闭 MPI, 标志并行代码段的结束 */ MPI_Finalize(); } /* main */

目的 用户自定义数据类型 / 派生数据类型 异构计算 : 不同系统有不同的数据表示格式 MPI 预先定义一些基本数据类型, 在实现过程中在这些基本数据类型为桥梁进行转换 派生数据类型 : 允许消息来自不连续的和类型不一致的存储区域, 如数组散元与结构类型等的传送 MPI 中所有数据类型均为 MPI 自定义类型 基本数据类型, 如 MPI_INT,MPI_DOUBLE 用户定义数据类型或派生数据类型

MPI 型MPI 并行程序设计 51/217 类型基本数据类

数据类型匹配规则 有类型数据的通信, 发送方和接收方均使用相同的数据类型 ; 无类型数据的通信, 发送方和接收方均以 MPI_BYTE 作为数据类型 ; 打包数据的通信, 发送方和接收方均使用 MPI_PACKED

集合通信 (Collective Communication) 特点 通信空间中的所有进程都参与通信操作 每一个进程都需要调用该操作函数 一到多 多到一 同步

MPI 集合通信函类型函数功能 All: 表示结果到所有所有进程 数据移动 MPI_Bcast 一到多, 数据广播 MPI_Gather MPI_Gatherv MPI_Allgather MPI_Allgatherv V:Variety, 被操作的数据对象和操作更为灵活 多到一, 数据汇合 MPI_Gather 的一般形式 MPI_Gather 的一般形式 MPI_Allgather 的一般形式 合通多到多, 置换数据 ( 全交换 ) 函数据聚集多到一, MPI_Scatter 一到多, 数据分散 MPI_Scatterv MPI_Scatter 的一般形式 MPI_Alltoall MPI_Reduce 数据归约数MPI_Alltoallv MPI_Alltoall 的一般形式 MPI_Allreduce MPI_Reduce 的一般形式, 结果在所有进程 MPI_Reduce_scatte r MPI_Scan 结果 scatter 到每个进程 前缀操作 同步 MPI_Barrier 同步操作

数据移动 Broadcast Scatter Gather Allgather Alltoall MPI 并行程序设计

数据聚集 Reduce Allreduce Reduce-scatter Scan MPI 预定义全局数据运算符 : MPI_MAX / MPI_MIN; MPI_SUM 求和 MPI_PROD PROD 求积 MPI_LAND 逻辑与 MPI_LOR 逻辑或 MPI _ MAXLOC/MPI _ MINL OC 最大 / 小值求下相应位置 MPI 并行程序设计 56/217

提纲 引言 认识 MPI 编程 MPI 编程简介 实例

实例分析 : 求 pi

串行代码

并行代码 #include <stdioh> #include <mpih> #include <mathh> h> long n, /*number of slices */ i; /* slice counter */ double sum, /* running sum */ pi, /* approximate value of pi */ mypi, x, /* independent var */ h; /* base of slice */ int group_size,my_rank; main(argc,argv) int argc; char* argv[];

{ int group_size,my_rank; MPI_Status status; MPI_Init(&argc,&argv); MPI_Comm_rank( MPI_COMM_WORLD, &my_rank); MPI_Comm_size( MPI_COMM_WORLD, &group_size); } n=2000; /* Broadcast n to all other nodes */ MPI_Bcast(&n,1,MPI_LONG,0,MPI_COMM_WORLD); h = 10/(double) n; sum = 00; for (i = my_rank+1; i< <= n; i += group_size) { x = h*(i-05); sum = sum +40/(10+x*x); } mypi = h*sum; /*Global sum */ MPI_Reduce(&mypi,&pi,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD); &pi if(my_rank==0) { /* Node 0 handles output */ printf("pi is approximately : %16lf\n",pi); } MPI_Finalize();

参考资料 Kai Hwang,Zhiwei Xu, 可扩展并行计算 : 技术 结构与编程 Michael J Quinn, MPI 与 OpenMP 并行程序设计 陈国良, 中国科技大学, 并行计算 - 结构 算法和编程 都志辉, 清华大学, MPI 并行程序设计 迟学斌, 中科院网络中心, 高性能并行计算 课程讲义 张林波, 中科院计算数学所, 并行计算 课程讲义 安虹, 中科大计算机系, 并行计算 课程讲义 曹振南, 曙光公司, MPI 并行程序设计讲义

谢谢

附录 隐式并行模型 程序员未作明确地指定并行性, 而让编译器和运行 ( 时 ) 支持系统自动地开拓它 显式并行模型 程序的并行性由程序员利用专门的语言结构, 编译知道和库函数调用等在源代码中给予明显的制定

什么是缓冲区? 应用程序中说明的变量, 在消息传递语句中又用作缓冲区的起始位置 也可表示由系统创建和管理的某一存储区域, 在消息传递过程中用于暂存放消息 也被称为系统缓冲区 用户可设置一定大小的存储区域, 用作中间缓冲区以保留可能出现在其应用程序中的任意消息

Broadcast -- 数据广播 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 = 10; MPI_Bcast(& (&buf buf,1,mpi_flo 1MPI AT,0, comm); Process 0 myrank = 0 data buf Process 1 myrank = 1 data Process p-1 myrank = p-1 data MPI_Bcast(); MPI_Bcast(); MPI_Bcast(); int MPI_Bcast ( void *buffer,/* 发送 / 接收 buf*/ int count, /* 元素个数 */ MPI_Datatype datatype, int root, /* 指定根进程 */ MPI_Comm comm) 根进程既是发送缓冲区也是接收缓冲区

Scatter -- 数据分散 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 buf,10,mpi_flo AT, data,10,mpi_float,,10,mpi_float,0,comm,comm ); Process 0 myrank = 0 data MPI_Scatter(); buf Process 1 myrank = 1 data MPI_ Scatter(); Process p-1 myrank = p-1 data MPI_ Scatter(); 根进程中存储了 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 )

Gather -- 数据收集 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 comm, &p); if(myrank==0) buf=(float*) =(float*)malloc(p*10* (p*10*s izeof(float); (float);/* 开辟接收缓冲区 */ MPI_Gather(data data,10,mpi_flo AT, buf,10,mpi_float,,10,mpi_float,0,comm);,comm); Process 0 myrank = 0 data MPI_Gather(); buf Process 1 myrank = 1 data MPI_Gather(); Process p-1 myrank = p-1 data MPI_Gather(); 根进程接收其他进程来的消息 ( 包括根进程 ), 按每在进程在通信组中的编号在通信组中的编号依次联接在一下, 存放在根进程的接收缓冲区中 int MPI_Gather ( void *sendbuf, int sendcnt, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm )

Reduce -- 全局数据运算 int p, myrank; float data = 00; 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 data,&,&buf buf,1,mp, I_FLOAT,MPI_SUM,0,comm ); Process 0 myrank = 0 data buf MPI_Scatter(); + Process 1 myrank = 1 data Process p-1 myrank = p-1 data MPI_ Scatter(); MPI_ Scatter(); 对组中所有进程的发送缓冲区中的数据用 OP 参数指定的操作进行运算, 并将结果送回到根进程的接收缓冲区中 int MPI_Reduce ( void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm )

后缀 V: 更灵活的集合通信 带后缀 V 的集合通信操作是一种更为灵活的集合通信操作 通信中元素块的大小可以变化 发送与接收时的数据位置可以不连续

MPI_Gather 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 通信空间

MPI_Gatherv 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 通信空间

Gather 与 GatherV Gather GatherV GatherV 应用 Vector 派生数据类型