MPI并行编程
并行计算_实验三_简单的MPI并行程序及性能分析
并行计算_实验三_简单的MPI并行程序及性能分析一、实验背景和目的MPI(Massive Parallel Interface,大规模并行接口)是一种用于进行并行计算的通信协议和编程模型。
它可以使不同进程在分布式计算机集群上进行通信和协同工作,实现并行计算的目的。
本实验将设计和实现一个简单的MPI并行程序,并通过性能分析来评估其并行计算的效果。
二、实验内容1.设计一个简单的MPI并行程序,并解决以下问题:a.将一个矩阵A进行分块存储,并将其均匀分配给不同的进程;b.将每个进程分别计算所分配的矩阵块的平均值,并将结果发送给主进程;c.主进程将收到的结果汇总计算出矩阵A的平均值。
2.运行该MPI程序,并记录下执行时间。
3.对程序的性能进行分析:a.利用不同规模的输入数据进行测试,观察程序的运行时间与输入规模的关系;b. 使用mpiexec命令调整进程数量,观察程序的运行时间与进程数量的关系。
三、实验步骤1.程序设计和实现:a.设计一个函数用于生成输入数据-矩阵A;b.编写MPI并行程序的代码,实现矩阵块的分配和计算;c.编写主函数,调用MPI相应函数,实现进程间的通信和数据汇总计算。
2.编译和运行程序:a.使用MPI编译器将MPI并行程序编译成可执行文件;b.在集群上运行程序,并记录下执行时间。
3.性能分析:a.对不同规模的输入数据运行程序,记录下不同规模下的运行时间;b. 使用mpiexec命令调整进程数量,对不同进程数量运行程序,记录下不同进程数量下的运行时间。
四、实验结果和分析执行实验后得到的结果:1.对不同规模的输入数据运行程序,记录下不同规模下的运行时间,得到如下结果:输入规模运行时间100x1002.345s200x2005.678s300x30011.234s...从结果可以看出,随着输入规模的增加,程序的运行时间也相应增加。
2. 使用mpiexec命令调整进程数量,对不同进程数量运行程序,记录下不同进程数量下的运行时间,得到如下结果:进程数量运行时间110.345s26.789s43.456s...从结果可以看出,随着进程数量的增加,程序的运行时间逐渐减少,但当进程数量超过一定限制后,进一步增加进程数量将不再显著减少运行时间。
MPI并行程序设计实例教程教学设计
MPI并行程序设计实例教程教学设计1. 简介MPI (Message Passing Interface) 是一种进程间通信的标准,可用于实现并行计算。
MPI 是一个库,通过对 MPI 中的函数调用,可实现在共享内存和分布式内存计算机中实现并行计算的任务分割和进程通信。
在实际应用中,MPI 会被和多线程一样用于实现算法的并行化,从而提高计算效率和运行速度。
2. 教学目标通过这个实例教程,我们会:1.了解 MPI 并行程序设计的基本概念和原理2.学会使用 MPI 的基本函数和指令3.学会通过实例演示的方式,掌握常见的 MPI 算法和技术4.实现一个简单的 MPI 并行程序,对其进行测试和优化,提高程序的执行效率3. 教学计划本教程共计 5 个部分,每个部分涵盖不同的内容。
每个部分的内容和学习目标如下:第一部分:MPI 基础概念和原理本部分的目标是让学生了解 MPI 的概念、原理和应用场景。
通过课堂讲授、案例分析和问题解答等方式,使学生领悟 MPI 的并行计算模型和通信方式。
第二部分:MPI 基本函数和指令本部分的目标是让学生掌握 MPI 中的基本函数和指令,理解其使用方法和调用方式。
通过讲解 MPI_Init、MPI_Comm_size、MPI_Comm_rank 等函数和指令,让学生能够熟练使用 MPI 构建并行程序。
第三部分:MPI 并行算法实例本部分的目标是让学生通过具体实例学习 MPI 并行算法设计的方法和技巧。
通过案例分析的方式,让学生了解 MPI 算法设计的核心思想、主要步骤和注意事项。
同时,本部分还会介绍一些常见的 MPI 库和工具,如 MPIBLAST 和 OpenMPI。
第四部分:MPI 程序设计和优化本部分的目标是让学生实践 MPI 代码的编写、调试和优化过程。
通过一个综合实例,让学生学习 MPI 并行程序的设计、实现和测试。
同时,本部分还会讲授MPI 排序算法和负载平衡算法的具体实现方法。
visual studio fortran mpi用法
在 Visual Studio 中使用 Fortran 和 MPI(Message Passing Interface)进行并行编程,可以通过以下步骤:1. 安装 Visual Studio:首先,确保已安装 Visual Studio。
您可以从 Microsoft 官方网站上下载适用于 Fortran 开发的 Visual Studio 版本。
2. 安装 Intel Fortran Compiler:如果您计划使用 Intel Fortran 编译器进行开发,需要安装 Intel Parallel Studio XE,其中包含了 Intel Fortran Compiler。
3. 创建 Fortran 项目:在 Visual Studio 中,选择 "创建新项目",然后选择 "Fortran" 类型的项目。
4. 配置项目属性:在项目属性中,设置编译器选项和链接器选项,以及其他项目设置。
例如,设置编译器为 Intel Fortran Compiler。
5. 编写 Fortran 代码:编写您的 Fortran 代码,可以使用 Fortran 90/95/2003/2008 的语法。
6. 添加 MPI 支持:在 Fortran 代码中,使用 MPI 库函数进行并行编程。
您需要添加MPI 头文件和库文件的路径,以便编译和链接 MPI 相关代码。
MPI 头文件通常位于MPI 安装目录的 include 文件夹下,而库文件则位于 lib(Windows)或 lib64(Linux)文件夹下。
7. 构建和运行:构建项目并运行生成的可执行文件。
在运行 MPI 程序时,您可能需要在启动命令中指定使用的进程数(例如,mpiexec -n 4)。
这些步骤提供了一个基本的框架,让您可以在 Visual Studio 中进行 Fortran 和 MPI 的并行编程。
根据您的具体需求和环境,可能还需要进行一些其他的配置和设置。
MPI编程简单介绍
MPI编程简单介绍MPI编程简单介绍多线程是⼀种便捷的模型,当中每⼀个线程都能够訪问其他线程的存储空间。
因此,这样的模型仅仅能在共享存储系统之间移植。
⼀般来讲,并⾏机不⼀定在各处理器之间共享存储,当⾯向⾮共享存储系统开发并⾏程序时,程序的各部分之间通过来回传递消息的⽅式通信。
要使得消息传递⽅式可移植,就须要採⽤标准的消息传递库。
这就促成的消息传递接⼝(Message Passing Interface, MPI)的⾯世,MPI是⼀种被⼴泛採⽤的消息传递标准[1]。
与OpenMP并⾏程序不同,MPI是⼀种基于消息传递的并⾏编程技术。
消息传递接⼝是⼀种编程接⼝标准,⽽不是⼀种详细的编程语⾔。
简⽽⾔之,MPI标准定义了⼀组具有可移植性的编程接⼝。
各个⼚商或组织遵循这些标准实现⾃⼰的MPI软件包,典型的实现包含开放源码的MPICH、LAM MPI以及不开放源码的Intel MPI。
因为MPI提供了统⼀的编程接⼝,程序猿仅仅须要设计好并⾏算法,使⽤对应的MPI库就能够实现基于消息传递的并⾏计算。
MPI⽀持多种操作系统,包含⼤多数的类UNIX和Windows系统。
3.1.1怎样实现MPI3.1.2 MPI程序的特点MPI程序是基于消息传递的并⾏程序。
消息传递指的是并⾏运⾏的各个进程具有⾃⼰独⽴的堆栈和代码段,作为互不相关的多个程序独⽴运⾏,进程之间的信息交互全然通过显⽰地调⽤通信函数来完毕。
的安装和配置我使⽤的MPICH2安装⽂件是mpich2-1.0.6p1-win32-ia32.msi,在Windows下安装MPICH2⽐較简单,可是要有Microsoft .NET Framework 2.0的⽀持。
安装基本上仅仅要单击“Next”就可以。
在安装过程中会提⽰输⼊进程管理器的password,这个password被⽤来訪问全部的程序,这⾥使⽤的password为admin。
安装完毕后,安装⽂件夹下的include⼦⽂件夹包括了编程所须要的全部头⽂件,lib⼦⽂件夹包括了对应的程序库,⽽⼦⽂件夹bin则包括了MPI在Windows以下必须的执⾏程序。
《并行编程原理与程序设计》课程教学大纲
本科生课程大纲课程属性:公共基础/通识教育/学科基础/专业知识/工作技能,课程性质:必修、选修一、课程介绍1.课程描述:《并行编程原理与程序设计》是地球信息科学与技术专业海洋测绘与地理信息系统方向的选修课,也是勘查技术与工程专业的选修课。
地球物理信息解译中的计算量十分庞大,常规串行电脑和软件无法解决地球物理资料的解译问题,必须采用并行算法合并行计算机来解决地球物理资料的处理、解释合反演工作。
目前,微机群和GPU机群在地球物理领域的应用日益广泛,“地球信息科学与技术”和“勘查技术与工程”专业必需掌握并行编程的基本原理与方法才能实现地学信息高效解译得目的,本课程主要学习基于微机群的MPI 程序设计方法和基于GPU集群的CUDA程序设计方法,并进行适当的上机实践。
通过本课程的学习,可使学生了解和掌握大型科学与工程问题中的基本并行编程技术,初步具备编写大型并行应用程序的能力。
2.设计思路:本课程的讲授内容主要包括两大部分:第一部分:MPI并行程序设计部分:第一章并行程序设计基础主要内容:并行计算;并行编程模型与并行语言;并行算法第二章 MPI简介主要内容:什么是MPI;MPI的目的,产生与发展;MPI的语言绑定;目前主要的MPI实现;SPMD并行机上并行程序的执行过程第三章第一个MPI程序主要内容:MPI实现的“Hello World”;c与Fortran语言的MPI程序的一些惯例第四章六个接口构成的MPI子集主要内容:子集介绍;MPI预定义的数据类型;MPI数据类型匹配与数据转换;MPI消息;第五章简单的MPI程序示例主要内容:获取机器名字和MPI版本号;数据接力传送;任意进程间互相问候,任意源和任意标识的使用;编写安全的MPI程序第六章 MPI并行程序的两种基本模式主要内容:对等模式的MPI程序设计;主从模式的MPI程序设计,标准通信模式的特点与消息传递过程第七章不同通信模式MPI并行程序设计主要内容:四种通信模式(标准,缓存,同步与就绪),了解集中通信模式的划分依据,掌握四种通信模式的优缺点及实现方式第八章非阻塞通信MPI程序设计主要内容:阻塞通信;非阻塞通信简介;非阻塞标准发送与接收;非阻塞通信与其他三种通信模式的结合;非阻塞通信的完成第九章组通信MPI程序设计主要内容:组通信的消息通信功能,同步功能和计算功能;广播;收集;散发;组收集;全互换、同步、归约、组归约、归约并散发操作的函数形式、使用方法与执行过程;几个相关示例程序第二部分:CUDA并行程序设计部分:第一章引言主要内容:异构并行算法,现代GPU的体系结构,为什么需要更高的速度和并行化,应用程序加速,并行编程语言和模型第二章 GPU计算的发展历程主要内容:图形流水线的发展,固定功能的图形流水线时代,可编程实时图形流水线的发展,图形与计算结合的处理器,GPGPU:一个中间步骤,GPU计算,可扩展的GPU,发展近况,未来的发展趋势第3章 CUDA简介主要内容:PC架构,GPU硬件结构,CPU与GPU,数据并行性,CUDA的程序结构第4章 CUDA环境搭建主要内容:简介,在Windows下安装软件开发工具包,Visual Studio,工程,64位用户,创建工程,Linux,安装调试器,编译模型,错误处理第5章线程网格、线程块以及线程,主要内容:简介,线程,问题分解,CPU与GPU的不同,任务执行模式,GPU 线程,CUDA内核,线程块,线程网格,跨幅与偏移,X与Y方向的线程索引,线程束,分支,GPU的利用率,线程块的调度第6章数据并行执行模型,主要内容:向量加法kernel函数,设备全局存储器与数据传输,kernel函数与线程,函数声明,启动kernel函数,预定义变量,CUDA的线程组织,线程与多维数据映射,矩阵乘法——一个更加复杂的kernel函数,线程同步和透明的可扩展性,线程块的资源分配,线程调度与容许时延第三部分:上机实践部分:本课程实践部分的设计思路为:以并行程序设计的方法为主线,结合地学信息处理中的实际问题,让学生掌握MPI和CUDA程序设计的基本方法和技能。
高性能计算中的并行编程模型介绍
高性能计算中的并行编程模型介绍高性能计算(High-Performance Computing,HPC)是一种利用大规模计算机系统进行高效计算和解决复杂问题的技术。
在高性能计算中,为了提高计算效率和处理大规模数据,使用并行编程模型是必不可少的。
并行编程模型是一种在多个处理单元(如CPU、GPU等)上同时执行代码的方法,能够实现任务的分解和并发执行,提高计算速度和系统的整体性能。
并行编程模型主要有以下几种:共享内存模型、分布式内存模型以及混合模型。
共享内存模型是指多个处理单元共享同一个内存空间,在该模型中,所有的处理单元可以同时访问和修改共享内存中的数据。
共享内存模型的最大优势在于简单易用,程序员只需要在编写代码时考虑数据的同步和互斥。
常用的共享内存编程模型包括OpenMP和POSIX线程。
OpenMP(Open Multi-Processing)是一种支持并行编程的API,可以通过在代码中添加一些特殊的指令来实现并行化。
通过使用OpenMP,程序员可以简单地将串行代码转化为并行代码。
OpenMP使用的指令主要包括#pragma omp并行指令、#pragmaomp for指令以及#pragma omp critical指令等。
这些指令可以指定代码块并行执行、循环并行化以及实现临界区保护等。
OpenMP适用于共享内存系统,对于多核CPU和SMP(Symmetric Multi-Processing)系统,具有较好的扩展性。
POSIX线程(Pthreads)是一种标准的共享内存并行编程模型,可以在多线程环境下创建和管理线程。
Pthreads使用的函数库包括pthread_create、pthread_join和pthread_mutex等,可以创建线程、等待线程结束并实现互斥和同步。
使用Pthreads编写的并行程序可以同时利用多个CPU核心进行计算,有效地提高了程序的执行速度。
分布式内存模型是指多个处理单元之间通过消息传递来共享数据,每个处理单元拥有自己的本地内存。
c语言mpi并行计算矩阵乘法
c语言mpi并行计算矩阵乘法
C语言中的MPI(Message Passing Interface)是一种用于编写并行程序的标准,它允许多个进程在不同的计算节点上进行通信和协作。
矩阵乘法是一个经典的并行计算问题,可以通过MPI来实现并行化。
首先,我们需要将矩阵乘法的计算任务分配给不同的进程。
可以将两个矩阵分别分块,然后将这些块分配给不同的进程。
每个进程负责计算其分配到的部分,并将结果发送回主进程。
在C语言中,可以使用MPI库来实现这一过程。
首先,需要初始化MPI环境,并确定每个进程的编号和总进程数。
然后,主进程可以负责将矩阵分块并发送给其他进程,其他进程则接收并进行计算。
计算完成后,结果可以发送回主进程,由主进程进行汇总。
需要注意的是,在并行计算中,需要考虑数据通信的开销以及负载均衡等问题。
合理地分配任务和减少通信开销是并行计算中需要考虑的重要问题。
另外,还可以考虑使用一些优化技术来提高并行矩阵乘法的性
能,比如使用非阻塞通信、优化数据布局以减少通信量、使用多级并行等技术。
总之,使用C语言和MPI实现矩阵乘法的并行计算需要考虑任务分配、数据通信、性能优化等多个方面,需要综合考虑并合理设计并行算法。
并行编程——MPIOPENMP混合编程
并⾏编程——MPIOPENMP混合编程在⼤规模节点间的并⾏时,由于节点间通讯的量是成平⽅项增长的,所以带宽很快就会显得不够。
所以⼀种思路增加程序效率线性的⽅法是⽤MPI/OPENMP混合编写并⾏部分。
这⼀部分其实在了解了MPI和OPENMP以后相对容易解决点。
⼤致思路是每个节点分配1-2个MPI进程后,每个MPI进程执⾏多个OPENMP线程。
OPENMP部分由于不需要进程间通信,直接通过内存共享⽅式交换信息,不⾛⽹络带宽,所以可以显著减少程序所需通讯的信息。
Fortran:Program hellouse mpiuse omp_libImplicit NoneInteger :: myid,numprocs,rc,ierrInteger :: i,j,k,tidCall MPI_INIT(ierr)Call MPI_COMM_RANK(MPI_COMM_WORLD,myid,ierr)Call MPI_COMM_SIZE(MPI_COMM_WORLD,numprocs,ierr)!$OMP Parallel private(tid)tid=OMP_GET_THREAD_NUM()write(*,*) 'hello from',tid,'of process',myid!$OMP END PARALLELCall MPI_FINALIZE(rc)StopEnd Program helloC++:# include <cstdlib># include <iostream># include <ctime># include "mpi.h"# include "omp.h"using namespace std;int main ( int argc, char *argv[] );//****************************************************************************80int main ( int argc, char *argv[] ){int myid;int nprocs;int this_thread;MPI::Init();myid=MPI::COMM_WORLD.Get_rank();nprocs=MPI::COMM_WORLD.Get_size();#pragma omp parallel private(this_thread){this_thread=omp_get_thread_num();cout <<this_thread<<" thread from "<<myid<<" is ok\n";}MPI::Finalize();return0;}这⾥值得要注意的是,似乎直接⽤mpif90/mpicxx编译的库会报错,所以需要⽤icc -openmp hello.cpp -o hello -DMPICH_IGNORE_CXX_SEEK -L/Path/to/mpi/lib/ -lmpi_mt -lmpiic -I/path/to/mpi/include其中-DMPICH_IGNORE_CXX_SEEK为防⽌MPI2协议中⼀个重复定义问题所使⽤的选项,为了保证线程安全,必须使⽤mpi_mt库对于intel的mpirun,必须在mpirun后加上-env I_MPI_PIN_DOMAIN omp使得每个mpi进程会启动openmp线程。
mpi
Message Passing Interface—消息传递接口
与OpenMP并行程序不同,MPI是一 种基于信息传递的并行编程技术。消息传 递接口是一种编程接口标准,而不是一种 具体的编程语言。简而言之,MPI标准定义 了一组具有可移植性的编程接口。
1.MPi组成
• 数据类型
定义了精确的数据类型参数而不使用字节计数,以数据类型为单位指定 消息的长度; 对于C和Fortran,MPI均预定义了一组数据类型和一些附加的数据类型; 可以发送 或接收连续的数据,还可以处理不连续的数据;中的一个通信域定义了一组进程和一个通信的上下文,虚拟处理 器拓扑、属性等内容。它以对象形式存在,作为通信操作的附加参数。 MPI预定义的通信域:mpi comm world(包含所有进程)、 mpi comm self(只包含各个进程自己的进程组)
• MPI调用接口—6个基本调用
1.mpi init()初始化MPI执行环境,建立多个MPI进程之间的联系,为 后续通信做准备 2.mpi finalize 结束MPI执行环境 3.mpi comm rank用来标识各个MPI进程的,给出调用该函数的进程的 进程号,返回整型的错误值 两个参数:MPI_Comm类型的通信域,标识参与计算的MPI进程组; &rank返回调用进程中的标识号 4.mpi comm size用来标识相应进程组中有多少个进程
• 组通信—一个特定组内所有进程都参加全局的数据处理和通信操作
功能:通信—组内数据的传输 同步—所有进程在特定的点上取得一致 计算—对给定的数据完成一定的操作 类型:1)数据移动 广播(mpi bcast) 收集(mpi gather) 散射(mpi scater) 组收集(mpi all gather)全交换(all to all) 2)聚集 规约(mpi reduce)将组内所有的进程输入 缓冲区中的数据按 定操作OP进行运算,并将起始结果返回到root进程的接收缓冲区 扫描(mpi scan)要求每一个进程对排在它前面的进程进行规约 操作,结果存入自身的输出缓冲区 3)同步 路障(mpi barrier)实现通信域内所有进程互相同步,它们将处 于等待状态,直到所有进程执行它们各自的MPI-BARRIER调用
c++的mpi编程
c++的mpi编程C++的MPI编程是一种用于实现并行计算的方法。
MPI(Message Passing Interface)是一种通信协议,它允许在分布式计算系统中进行高效的消息传递。
在C++中使用MPI编程,可以充分利用多核处理器和集群计算机等并行资源。
以下是C++ MPI编程的基本步骤:1. 配置MPI环境:首先,需要在操作系统上安装MPI库。
安装完成后,可以通过编译器参数或环境变量指定MPI库的路径。
2. 编写程序:编写一个C++程序,使用MPI库提供的函数进行通信和同步。
常见的MPI函数包括发送和接收消息的函数(如MPI_Send 和MPI_Recv),以及用于进程间通信的函数(如MPI_Comm_size和MPI_Comm_rank)。
3. 分配计算资源:在程序中,使用MPI_Comm_size函数获取进程数量,然后根据问题规模和计算资源分配任务。
可以使用MPI_Comm_rank函数获取当前进程的编号。
4. 初始化MPI:在程序开始时,使用MPI_Init函数初始化MPI库。
在程序结束时,使用MPI_Finalize函数关闭MPI库。
5. 编写主函数:在主函数中,使用MPI函数进行进程间通信和数据交换。
例如,可以使用MPI_Send和MPI_Recv函数实现数据的发送和接收。
6. 编写并行区域:在并行区域中,使用MPI_Barrier函数同步进程。
这样可以确保所有进程在执行并行区域之前达到相同的进度。
7. 结束MPI:在程序结束时,使用MPI_Finalize函数关闭MPI库。
以下是一个简单的C++ MPI编程示例:```cpp#include <iostream>#include <mpi.h>int main(int argc, char *argv[]) {// 初始化MPI库MPI_Init(&argc, &argv);// 获取进程数量和当前进程编号int size, rank;MPI_Comm_size(MPI_COMM_WORLD, &size);MPI_Comm_rank(MPI_COMM_WORLD, &rank);// 输出进程信息std::cout << "Hello, I am process " << rank << " of " << size << "." << std::endl;// 结束MPI库MPI_Finalize();return 0;}```这个示例程序将输出各个进程的编号和总进程数。
MPI并行编程入门
S SISD
S I
MISD
M 指令个数
SMP- Symmetric MultiProcessing
多个CPU连接于统一的内存总线 内存地址统一编址,单一操作系统映像 可扩展性较差,一般CPU个数少于32个 目前商用服务器多采用这种架构
聚集方式:
归约
扫描
通信模式
一对一:点到点(point to point) 一对多:广播(broadcast),播撒(scatter) 多对一:收集(gather), 归约(reduce) 多对多:全交换(Tatal Exchange), 扫描(scan) , 置
换/移位(permutation/shift)
Work Pool
P1
P2
并行算法
• 并行算法设计基本原则
– 与体系结构相结合——线性结构,二维网络结 构……
– 具有可扩展性——并行算法是否随处理机个数 增加而能够线性或近似线性的加速
– 粗粒度——通常情况,粒度越大越好 – 减少通信——减少通信量和通信次数 – 优化性能——单机计算效率和并行效率
流水线计算示意图
并行化分解方法
– 分而治之方法:
• 以一个简单的求和问题为例,说明什么是分而治之方法。假设在q = 2*2*2个处理机上计算:
可以将计算依次分解为两个小的求和问题,用下图简单的描述(图中给出 的是处理机号)。在图中,从上至下是分解的过程,从下至上是求部分 和的过程。这就是有分有治的一个简单过程,也既是一种分而治之方法。
sp (q) q
–
MPI并行编程
2.1 MPICH的实验环境
实验环境: RedHat 9.0 + Vmware 6.0 + MPICH 2-1.0 Fedora 14+ Dell Blade*1(主节点)+Dell PC*2 (从节点) +MPICH 2-1.2.1p1
2.2 MPICH的搭建步骤
[dair@node01 ~]$ mpicc -o hello hello.c [dair@node01 ~]$ ./hello ( ) [0] Aborting program ! Could not create p4 procgroup. Possible missing fileor program started without mpirun. [dair@node01 ~]$ mpirun -np 4 hello Hello World! Hello World! Hello World! Hello World! [dair@node01 ~]$
MPI并行编程
大纲
• • • • MPI并行编程简介 Linux下MPI并行编程环境的搭建 MPI并行程序设计 实例分析:矩阵乘法
1.1 什么是并行计算
进程 1 进程 2
传统的串行计算 串行计算,分为“指令” 串行计算 和“数据”两个部分,并在程序 执行时“独立地申请和占有”内 存空间,且所有计算均局限于 该内存空间。
计算机打印字符 我们输入的命令
3.6.1 MPI消息
• 消息:指在进程间进行的一次数据交换。 • 一个消息由源地址、数据个数、数据类型、目标地址、 消息标识和通信体构成。 • 消息包括信封和数据两个部分,信封指出了发送或接收 消息的对象及相关信息,而数据是本消息将要传递的内 容。
高性能计算中的MPI并行编程方法
高性能计算中的MPI并行编程方法随着科技的快速发展,计算机的性能和运算速度也越来越快。
然而,在面对海量数据和复杂运算时,单台计算机的性能已经无法满足需求。
因此,高性能计算(High Performance Computing, HPC)逐渐成为科学研究和工程领域中必不可少的一部分。
在HPC领域中,MPI并行编程是一种被广泛使用的技术,能够有效地提高计算机的并行性和计算速度。
MPI并行编程是什么?MPI(Message Passing Interface)是一种并行编程的通信库。
在MPI中,所有进程之间都是通过发送和接收消息来通信的。
MPI并行编程在计算机集群(Cluster)中应用广泛,因为计算机集群中的计算节点是分布在不同的物理服务器上,MPI并行编程可以将这些节点组合成一个逻辑上的整体,有效地提高了计算机的并行性。
MPI并行编程的优点MPI并行编程有如下优点:1.能够实现海量数据和复杂运算的高效处理,提高了计算速度。
2.可靠性高,因为MPI并行编程中所有进程之间都是通过发送和接收消息来通信的,不依赖于共享内存,可以有效避免内存泄漏等问题。
3.良好的可移植性,MPI API已经被很多计算机系统所支持,使得代码在不同的平台上也可以运行。
MPI并行编程的应用领域MPI并行编程被广泛应用于高性能计算领域,包括海洋模拟、气象预报、金融计算、生物医学等多个领域。
在气象预报中,MPI并行计算可以对气象数据进行分布式处理,快速生成气象预报模型,为人们提供准确的天气信息。
在金融领域,MPI并行编程可以进行股票行情分析、期货合约计算等任务,为投资者提供科学的决策支持。
在生物医学领域,MPI并行编程可以进行基因序列比对、药物分子模拟等任务,为科学家们提供准确的实验数据。
MPI并行编程的实现方法MPI通信和计算模型是一种消息传递模型,MPI中进程之间的通信主要与其它进程的交互有关。
在MPI并行编程中,进程的编写过程可以被分为以下几个步骤:1.初始化MPI环境在编写MPI程序时,必须首先调用MPI环境的初始化函数,建立进程之间的通讯。
并行计算中的MPI编程技巧及实践经验总结
并行计算中的MPI编程技巧及实践经验总结在并行计算中,MPI(Message Passing Interface)已成为一种广泛应用的并行编程模型。
MPI是一套并行通信协议和编程规范的集合,它可以在分布式内存系统中进行多个进程之间的通信和数据传输。
本文将总结一些MPI编程的技巧和实践经验,帮助开发人员更好地理解和应用MPI,以提高并行计算的效率和性能。
首先,了解MPI的基本概念和术语是理解和应用MPI编程的关键。
MPI中最重要的概念是通信域(communicator)。
通信域是一组进程的集合,这些进程可以相互通信。
在MPI中,有一个默认的通信域MPI_COMM_WORLD,它包含所有参与并行计算的进程。
另外,还有一些常用的通信域操作函数,例如MPI_Comm_size和MPI_Comm_rank函数,用于获取通信域中的进程数和当前进程的标识。
熟悉这些概念和函数,有助于编写可靠和高效的MPI程序。
其次,合理划分任务和数据,对于并行计算的效率至关重要。
在MPI中,一种常见的并行模式是将任务划分为多个子任务,并由不同进程来处理。
例如,可以将一个大规模的计算问题分解为多个小规模的子问题,由多个进程并行计算,最后将结果汇总。
另外,还可以将数据划分为多个块,分配给不同的进程进行处理。
合理的任务和数据划分可以最大程度地利用并行计算资源,提高计算效率和性能。
在MPI编程中,注意避免过多的通信操作,尽量减少进程间的通信次数和数据传输量。
频繁的通信操作会增加通信开销,降低程序的效率。
因此,可以通过合并通信操作,减少数据传输的次数。
例如,可以将多个小消息合并为一个大消息,进行一次批量传输,而不是每个小消息都单独进行传输。
此外,还可以使用非阻塞通信函数,例如MPI_Isend和MPI_Irecv函数,来实现异步通信,减少通信的等待时间。
另一个需要注意的是并行计算中的负载均衡。
负载均衡是指保证多个进程之间的工作量大致相等,避免某些进程一直处于空闲状态,导致计算效率低下。
高性能计算--MPI并行编程
⾼性能计算--MPI并⾏编程MPI常⽤函数MPI_Init(&argc, &argv)来初始化MPI环境,可能是⼀些全局变量的初始化。
MPI程序的第⼀个调⽤,它完成MPI程序所有的初始化⼯作,所有MPI程序的第⼀条可执⾏语句都是这条语句。
MPI_Comm_rank(communicator, &myid)来获取当前进程在通信器中具有的进程号。
不同的进程就可以将⾃⾝和其它的进程区别开来,实现各进程的并⾏和协作。
MPI_Comm_size(communicator, &numprocs)来获取通信器中包含的进程数⽬。
不同的进程通过这⼀调⽤得知在给定的通信域中⼀共有多少个进程在并⾏执⾏。
MPI_Finalize()来结束并⾏编程环境。
之后我们就可以创建新的MPI编程环境了。
MPI程序的最后⼀个调⽤,它结束MPI程序的运⾏,它是MPI程序的最后⼀条可执⾏语句,否则程序的运⾏结果是不可预知的。
int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)将发送缓冲区中的count个datatype数据类型的数据发送到⽬的进程,⽬的进程在通信域中的标识号是dest,本次发送的消息标志是tag,使⽤这⼀标志,就可以把本次发送的消息和本进程向同⼀⽬的进程发送的其它消息区别开来。
发送缓冲区是由count个类型为datatype的连续数据空间组成,起始地址为buf。
注意这⾥不是以字节计数,⽽是以数据类型为单位指定消息的长度。
int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source, int tag,MPI_Comm comm, MPI_Status *status)从指定的进程source接收消息,并且该消息的数据类型和消息标识和本接收进程指定的datatype和tag相⼀致,接收到的消息所包含的数据元素的个数最多不能超过count。
跨平台并行编程框架
跨平台并行编程框架跨平台并行编程框架跨平台并行编程框架是一种能够在不同操作系统和硬件平台上实现并行计算的框架。
随着计算能力的不断提升,单个计算机的性能已经不能满足大规模数据处理和计算的需求。
跨平台并行编程框架的出现,为开发人员提供了一种便捷有效的方式来利用多台计算机和多核处理器并行执行任务。
跨平台并行编程框架的核心思想是将任务分解为多个可并行执行的子任务,然后将这些子任务分配到不同的计算机或处理器上进行计算。
框架提供了一套接口和工具,使得开发人员能够方便地在不同的平台上编写并行代码,并自动处理任务分配、数据传输、同步和结果合并等问题。
一种常用的跨平台并行编程框架是OpenMP(Open Multi-Processing),它是一套基于共享内存的并行编程接口,可以在不同的操作系统上使用。
开----宋停云与您分享----发人员只需要在代码中添加一些指令,就可以实现并行执行。
OpenMP可以方便地利用多核处理器的计算能力,并且能够自动处理任务分配和同步操作,大大减少了并行编程的复杂性。
另外一种常用的跨平台并行编程框架是MPI (Message Passing Interface),它是一种基于消息传递的并行编程模型,可以在分布式计算环境下使用。
MPI框架提供了一系列的函数和库,用于在多台计算机之间传递消息和进行同步操作。
开发人员可以通过编写MPI程序,在不同的计算机上同时执行同一个任务,每个计算机地进行计算,并通过消息传递来进行协作。
跨平台并行编程框架的出现,极大地提高了并行计算的效率和可扩展性。
开发人员可以利用多台计算机和多核处理器进行大规模的数据处理和计算,大大缩短了任务的执行时间。
同时,跨平台并行编程框架还能够提高系统的可靠性和容错性,即使某台计算机出现故障,任务仍然可以在其他计算机上继续执行。
----宋停云与您分享----总之,跨平台并行编程框架为开发人员提供了一种方便、高效的方式来利用多台计算机和多核处理器进行并行计算。
消息传递编程接口MPI
并行矩阵乘法
并行矩阵乘法是MPI的重要应用之一, 通过将矩阵划分为多个块,并在不同的 处理器上并行计算这些块之间的乘积, 最终得到完整的矩阵乘积。
MPI提供了各种函数,如`MPI_Matmul`, 用于在进程之间传递矩阵数据并进行乘法运 算。
并行矩阵乘法在高性能计算、机器 学习等领域有广泛应用,能够显著 提高计算速度和效率。
错误处理和调试
错误检测和处理
MPI提供了错误检测机制,允许程序员在运行时检测和 处理错误。例如,当发送进程尝试发送消息给不存在的 接收进程时,MPI会返回一个错误码,提示发送进程操 作失败。
调试工具
为了帮助程序员调试并行程序,MPI提供了一系列的调 试工具和技术。这些工具可以帮助程序员跟踪程序的执 行过程、检查变量的状态以及诊断潜在的错误来源。通 过使用这些工具,程序员可以提高程序的可靠性和可维 护性。
03 MPI编程模型
点对点通信
进程间一对一通信
MPI提供了点对点通信方式,允许两个进程 之间直接发送和接收消息。这种方式适用于 一对一的通信场景,如进程间数据交换或同 步。
通信模式
点对点通信支持同步和异步模式。在同步模 式下,发送进程会等待消息被接收后才继续 执行;在异步模式下,发送进程发送消息后 可以继续执行其他任务,不需要等待接收进
并行排序
01
02
03
并行排序是MPI的另一个常见应用, 通过将待排序数据划分为多个子集, 并在不同的处理器上并行进行排序, 最终合并得到完全排序的结果。
MPI提供了各种函数,如`MPI_Sort`, 用于在进程之间传递数据并进行排序操 作。
并行排序在处理大规模数据集时具有 显著优势,能够显著提高排序速度和 效率。
计算空气动力学并行编程基础
计算空气动力学并行编程基础
空气动力学并行编程基础是指在进行空气动力学模拟和计算时,使用
并行编程技术来提高计算效率和准确性的基础知识和技能。
以下是空气动
力学并行编程基础的主要内容:
1.并行计算的原理和方法:了解并行计算的基本原理和方法,掌握基
于多核CPU、GPU等硬件加速器的并行计算方法。
2. 数值计算库的使用:掌握数值计算库(如OpenFOAM、ANSYS
Fluent等)的使用方法,能够将计算工作划分为多个任务进行并行处理。
3. MPI编程:学习使用MPI(Message Passing Interface)进行并
行编程,能够完成基于MPI的并行化计算程序的设计和开发。
4. 多线程编程:学习使用多线程技术进行并行编程,能够编写基于OpenMP或CUDA等多线程编程工具的程序。
5.分布式计算:学习使用分布式计算技术进行并行计算,了解如何搭
建分布式计算环境和编写基于分布式计算框架的程序。
6.网格划分与负载均衡:了解空气动力学计算中网格的划分和负载均
衡问题,能够设计和实现均衡的网格划分和负载均衡算法。
7.性能优化:了解空气动力学计算中的性能优化技术,如算法优化、
数据结构优化、计算资源调度等,能够优化计算程序的性能。
通过学习和掌握上述基础知识和技能,能够有效地利用并行计算技术
进行空气动力学计算和模拟,提高计算效率和准确性。
mpi应用实例
mpi应用实例并行计算是一种可以使用多个处理器或计算机来同时执行计算任务的方法。
MPI(Message Passing Interface)是一种常用的并行编程接口,它提供了一套标准的函数库,用于在多个计算节点之间进行消息传递和同步操作。
在本篇文章中,我们将介绍几个MPI应用实例,展示MPI在各个领域的应用和使用方法。
1. 分布式图像处理图像处理是计算密集型的任务,可以充分利用MPI的并行计算能力来加速处理过程。
例如,在一组图像上进行滤波操作,可以使用MPI将每个图像分发到不同的计算节点上进行并行处理。
每个节点负责处理分配给它的图像,并将处理结果传回主节点进行合并和输出。
通过这种方式,可以显著缩短处理时间,并提高图像处理的效率。
2. 并行矩阵计算在科学计算中,矩阵计算是一项常见的任务。
通过使用MPI,可以将矩阵分成多个子矩阵,分配给不同的计算节点进行并行计算。
每个计算节点可以根据需要进行计算,并将计算结果传回主节点进行最终的结果合并。
这种并行计算方法可以大大提高矩阵计算的速度和效率,特别是在处理大规模矩阵时效果更加明显。
3. 并行粒子模拟粒子模拟是一种常见的科学计算方法,用于模拟分子、原子、粒子等微观对象的运动和相互作用。
通过使用MPI,可以将模拟空间划分成多个区域,并将每个区域分配给不同的计算节点进行并行模拟。
每个节点负责模拟分配给它的区域,并与相邻节点进行通信和数据交换。
通过这种并行模拟的方式,可以加速粒子模拟的运算速度,提高模拟的精度和可靠性。
4. 并行排序算法排序是一项常见的计算任务,可以使用并行计算来加速排序过程。
通过使用MPI,可以将排序任务划分成多个子任务,分配给不同的计算节点进行并行排序。
每个节点负责排序分配给它的子任务,并将排序结果传回主节点进行最终的结果合并。
这种并行排序的方法可以显著提高排序的速度和效率,并在处理大规模数据集时具有较好的可扩展性。
在以上几个应用实例中,我们展示了MPI在不同领域的应用和使用方法。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
MPI2008 6 11i121.1 (2)1.1.1 (2)1.1.2 (3)1.1.3 (3)1.1.4MPI LB MPI UB (4)1.1.5 (5)1.2 (6)1.2.1MPI Type contiguous (6)1.2.2MPI Type vector (6)1.2.3MPI Type hvector (7)1.2.4MPI Type indexed (8)1.2.5MPI Type hindexed (9)1.2.6MPI Type struct (9)1.2.7 MPI Address (10)1.3 (11)1.3.1 (11)1.3.2 (11)1.3.3MPI Get elements (13)1.4 (13)1.4.1 (13)1.4.2 (13)1.4.3 (14)1.5MPI1.1 (15)162.1 (16)2.2 (17)2.2.1 MPI (17)ii2.2.2 MPI (18)2.2.3 (18)2.2.4 (18)2.2.5 (19)2.2.6 (19)2.3 (19)2.3.1 (19)2.3.2 (19)2.4 (20)2.4.1 (20)2.4.2 (21)2.4.3 (21)2.5 (21)2.5.1 (22)2.5.2 (23)2.5.3 (23)2.5.4 (24)2.5.5 (24)2.6 (25)2.6.1 (25)2.6.2 (25)2.6.3 (26)2.7 (26)2.7.1 (26)2.7.2 atomicity (27)2.7.3 (27)2.8 (28)iii, , . , .iv• , MPI , ,2001.• MPI 2001• 2006.••ftp:///pub/home/zlb/bxjs/bxjs.pdf•ftp:///pub/home/zlb/bxjs/1MPI . , (datatype) . MPI , , , / .§1.1§1.1.1MPI n ,n . , (type signature):Typesig={type0,type1,...,type n−1}., (type displacements):Typedisp={disp0,disp1,...,disp n−1}., ., , , MPI . , .. .Typemap={(type0,disp0),(type1,disp1),...,(type n−1,disp n−1)}.(type map). buff, n , i buff+disp i, type i,i=0,1,...,n−1.MPI {( ,0)}. MPI_INTEGER {(INTEGER,0)}., . , .1.1 TYPE{(REAL,4),(REAL,12),(REAL,0)}:REAL A(100)......CALL MPI_SEND(A,1,TYPE,...)A(2),A(4),A(1).21.2 TYPE{(REAL,−4),(REAL,0),(REAL,4)}:REAL A(3)......CALL MPI_SEND(A(2),1,TYPE,...)A(1),A(2),A(3).§1.1.2(size) ( ), . . type :{(type0,disp0),(type1,disp1),...,(type n−1,disp n−1)}:sizeof(type)=n−1∑i=0sizeof(type i)§1.1.3(lower bound) . (upper bound) 1, (alignment) ε. (extent) :lb(type)=mini{disp i}ub(type)=maxi{disp i+sizeof(type i)}+εextent(type)=ub(type)−lb(type), ε extent .: , . ( ) .1.3C#include<stdio.h>typedef struct{double d;char c;}CS;typedef struct{char c1;double d;3char c2;}CS1;main(){CS a;CS1b;printf("sizeof(CS)=%d\n",sizeof(CS));printf("offset(a.d)=%d,offset(a.c)=%d\n",(char*)&a.d-(char*)&a,(char*)&a.c-(char*)&a);printf("sizeof(CS1)=%d\n",sizeof(CS1));printf("offset(b.c1)=%d,offset(b.d)=%d,offset(b.c2)=%d\n",(char*)&b.c1-(char*)&b,(char*)&b.d-(char*)&b,(char*)&b.c2-(char*)&b);}1.4 MPI_DOUBLE_PRECISION MPI_INTEGER 4,MPI_BYTE 1, :{(MPI_DOUBLE_PRECISION,0),(MPI_INTEGER,8),(MPI_BYTE,12)}4, 0, 16, 16,ε=3.: 04-ex1.f§1.1.4MPI LB MPI UBMPI MPI_LB MPI_UB, (pseudo datatype). 0,4. .MPI : MPI_LB, :{disp i|type i=MPI_LB};lb(type)=mini, MPI_UB, :{disp i|type i=MPI_UB}.ub(type)=maxi1.5{(MPI_LB,−4),(MPI_UB,20),(MPI_DOUBLE_PRECISION,0),(MPI_INTEGER,8),(MPI_BYTE,12)}-4, 20, 24.MPI LB MPI UB : 04-ex2.f§1.1.5:Cint MPI_Type_size(MPI_Datatype datatype,int*size)int MPI_Type_extent(MPI_Datatype datatype,MPI_Aint*extent)int MPI_Type_ub(MPI_Datatype datatype,MPI_Aint*displacement)int MPI_Type_lb(MPI_Datatype datatype,MPI_Aint*displacement)Fortran77MPI_TYPE_SIZE(DATATYPE,SIZE,IERR)INTEGER DATATYPE,SIZE,IERRMPI_TYPE_EXTENT(DATATYPE,EXTENT,IERR)INTEGER DATATYPE,EXTENT,IERRMPI_TYPE_UB(DATATYPE,DISPLACEMENT,IERR)INTEGER DATATYPE,DISPLACEMENT,IERRMPI_TYPE_LB(DATATYPE,DISPLACEMENT,IERR)INTEGER DATATYPE,DISPLACEMENT,IERR5§1.2§1.2.1MPI Type contiguousCint MPI_Type_contiguous(int count,MPI_Datatype oldtype,MPI_Datatype*newtype)Fortran77MPI_TYPE_CONTIGUOUS(COUNT,OLDTYPE,NEWTYPE,IERR)INTEGER COUNT,OLDTYPE,NEWTYPE,IERRnewtype count oldtype (extent) .1.6Fortran77 :CALL MPI_SEND(BUFF,COUNT,TYPE,...):CALL MPI_TYPE_CONTIGUOUS(COUNT,TYPE,NEWTYPE,IERR)CALL MPI_TYPE_COMMIT(NEWTYPE,IERR)CALL MPI_SEND(BUFF,1,NEWTYPE,...).§1.2.2MPI Type vectorCint MPI_Type_vector(int count,int blocklength,int stride,MPI_Datatype oldtype,MPI_Datatype*newtype)Fortran77MPI_TYPE_VECTOR(COUNT,BLOCKLENGTH,STRIDE,OLDTYPE,+NEWTYPE,IERR)INTEGER COUNT,BLOCKLENGTH,STRIDE,OLDTYPE,NEWTYPE,+IERRnewtype count , blocklength oldtype , stride×extent(oldtype) .1.7DOUBLE PRECISION U(0:N,0:M)......CALL MPI_TYPE_VECTOR(M-1,N-1,N+1,+MPI_DOUBLE_PRECISION,NEWTYPE,IERR)CALL MPI_TYPE_COMMIT(NEWTYPE,IERR)CALL MPI_SEND(U(1,1),1,NEWTYPE,...)U(1:N-1,1:M-1).6: U ? U ? U ?§1.2.3MPI Type hvectorCint MPI_Type_hvector(int count,int blocklength,MPI_Aint stride,MPI_Datatype oldtype,MPI_Datatype*newtype)Fortran77MPI_TYPE_HVECTOR(COUNT,BLOCKLENGTH,STRIDE,OLDTYPE,+NEWTYPE,IERR)INTEGER COUNT,BLOCKLENGTH,STRIDE,OLDTYPE,NEWTYPE,+IERRnewtype count , blocklength oldtype , stride .MPI_Type_hvector MPI_Type_vector :stride MPI_Type_vector oldtype , MPI_Type_hvector .1.8DOUBLE PRECISION A(N,M),B(M,N)INTEGER TYPE1,TYPE2,STATUS(MPI_STATUS_SIZE)......CALL MPI_TYPE_VECTOR(N,1,M,MPI_DOUBLE_PRECISION,+TYPE1,IERR)CALL MPI_TYPE_EXTENT(MPI_DOUBLE_PRECISION,I,IERR)CALL MPI_TYPE_HVECTOR(M,1,I,TYPE1,TYPE2,IERR)CALL MPI_TYPE_FREE(TYPE1,IERR)CALL MPI_TYPE_COMMIT(TYPE2,IERR)CALL MPI_SENDRECV(A,N*M,MPI_DOUBLE_PRECISION,0,111,+B,1,TYPE2,0,111,+MPI_COMM_SELF,STATUS,IERR)CALL MPI_TYPE_FREE(TYPE2,IERR)A B . : 04-ex3.f7§1.2.4MPI Type indexedCint MPI_Type_indexed(int count,int*array_of_blocklengths,int*array_of_displacements,MPI_Datatype oldtype,MPI_Datatype*newtype)Fortran77MPI_TYPE_INDEXED(COUNT,ARRAY_OF_BLOCKLENGTHS,+ARRAY_OF_DISPLACEMENTS,OLDTYPE,NEWTYPE,+IERR)INTEGER COUNT,ARRAY_OF_BLOCKLENGTHS(*),+ARRAY_OF_DISPLACEMENTS(*),OLDTYPE,NEWTYPE,+IERRnewtype count . i array_of_blocklengths(i) oldtype, array_of_displacements(i)×extent(oldtype).MPI_Type_indexed MPI_Type_vector , .1.9REAL A(N,N)INTEGER LEN(N),DISP(N),TYPE......LEN(1)=NDISP(1)=0DO J=2,NLEN(J)=LEN(J-1)-1DISP(J)=(J-1)*(N+1)ENDDOCALL MPI_TYPE_INDEXED(N,LEN,DISP,MPI_REAL,TYPE,IERR)CALL MPI_TYPE_COMMIT(TYPE,IERR)CALL MPI_SEND(A,1,TYPE,...)......A .8§1.2.5MPI Type hindexedCint MPI_Type_hindexed(int count,int*array_of_blocklengths,MPI_Aint*array_of_displacements,MPI_Datatype oldtype,MPI_Datatype*newtype)Fortran77MPI_TYPE_HINDEXED(COUNT,ARRAY_OF_BLOCKLENGTHS,+ARRAY_OF_DISPLACEMENTS,OLDTYPE,NEWTYPE,IERR)INTEGER COUNT,ARRAY_OF_BLOCKLENGTHS(*),+ARRAY_OF_DISPLACEMENTS(*),OLDTYPE,NEWTYPE,+IERRnewtype count . i array_of_blocklengths(i) oldtype, array_of_displacements(i).MPI_Type_hindexed MPI_Type_indexed MPI_Type_hindexed array_of_displacements .§1.2.6MPI Type structCint MPI_Type_struct(int count,int*array_of_blocklengths,MPI_Aint*array_of_displacements,MPI_Datatype*array_of_types,MPI_Datatype*newtype)Fortran77MPI_TYPE_STRUCT(COUNT,ARRAY_OF_BLOCKLENGTHS,+ARRAY_OF_DISPLACEMENTS,ARRAY_OF_TYPES,+NEWTYPE,IERR)INTEGER COUNT,ARRAY_OF_BLOCKLENGTHS(*),+ARRAY_OF_DISPLACEMENTS(*),ARRAY_OF_TYPES(*),9+NEWTYPE,IERRnewtype count . i array_of_blocklengths(i) array_of_types(i) , array_of_displacements(i).MPI_Type_struct MPI_Type_hindexed .§1.2.7 MPI AddressMPI Address , Fortran77 .Cint MPI_Address(void*buff,MPI_Aint*address)Fortran77MPI_ADDRESS(BUFF,ADDRESS,IERR)<type>BUFF(*)INTEGER ADDRESS,IERRC , Fortran77 . Fortran77 MPI_ADDRESS ,MPI MPI_BOTTOM, 0, , :CALL MPI_ADDRESS(BUFF,ADDRESS,IERR),MPI_BOTTOM(ADDRESS) BUFF .1.10REAL A(N),B(N)INTEGER TYPE,IA,IB......CALL MPI_ADDRESS(A,IA,IERR)CALL MPI_ADDRESS(B,IB,IERR)CALL MPI_TYPE_HVECTOR(2,N,IB-IA,MPI_REAL,TYPE,IERR)......CALL MPI_SEND(A,1,TYPE,...)......A B.1.11PARAMETER(N=1024,M=512,L=256)REAL A(N)INTEGER B(M)COMPLEX C(L)INTEGER LEN(3),DISP(3),TYPE(3),NEWTYPE,IA,IB,ICDATA TYPE/MPI_REAL,MPI_INTEGER,MPI_COMPLEX/,+LEN/N,M,L/......CALL MPI_ADDRESS(A,IA,IERR)CALL MPI_ADDRESS(B,IB,IERR)CALL MPI_ADDRESS(C,IC,IERR)DISPS(1)=010DISPS(2)=IB-IADISPS(3)=IC-IACALL MPI_TYPE_STRUCT(3,LEN,DISP,TYPE,NEWTYPE,IER)......CALL MPI_SEND(A,1,NEWTYPE,...)......A,B C.1.12 1.11 :......DISPS(1)=IADISPS(2)=IBDISPS(3)=ICCALL MPI_TYPE_STRUCT(3,LEN,DISP,TYPE,NEWTYPE,IER)......CALL MPI_SEND(MPI_BOTTOM,1,NEWTYPE,...)......§1.3, MPI_Type_commit . ( ) MPI_Type_free , .§1.3.1Cint MPI_Type_commit(MPI_Datatype*datatype)Fortran77MPI_TYPE_COMMIT(DATATYPE,IERR)INTEGER DATATYPE,IERRMPI ., , .§1.3.2Cint MPI_Type_free(MPI_Datatype*datatype)Fortran77MPI_TYPE_FREE(DATATYPE,IERR)INTEGER DATATYPE,IERR11MPI_Type_free . ,datatype MPI_DATATYPE_NULL. . .1.13REAL U(N,M),V(N,M)INTEGER IU,IV,CTYPE,LTYPE......CALL MPI_ADDRESS(U,IU,IERR)CALL MPI_ADDRESS(V,IV,IERR)CALL MPI_TYPE_VECTOR(M,1,N,MPI_REAL,CTYPE,IERR)CALL MPI_TYPE_HVECTOR(2,1,IV-IU,CTYPE,LTYPE,IERR)CALL MPI_TYPE_FREE(CTYPE,IERR)CALL MPI_TYPE_HVECTOR(2,N,IV-IU,MPI_REAL,CTYPE,IERR)......! U(1:N,1),V(1:N,1)CALL MPI_SEND(U(1,1),1,CTYPE,...)! U(1,1:M),V(1,1:M)CALL MPI_SEND(U(1,1),1,LTYPE,...)! U(1:N,M),V(1:N,M)CALL MPI_SEND(U(1,M),1,CTYPE,...)! U(N,1:M),V(N,1:M)CALL MPI_SEND(U(N,1),1,LTYPE,...)......1.14REAL U(N,M),V(N,M)INTEGER IU,IV,EX,CTYPE,LTYPEINTEGER LENS(3),DISPS(3),TYPES(3)DATA LENS/1,1,1/,TYPES/MPI_REAL,MPI_REAL,MPI_UB/......CALL MPI_ADDRESS(U,IU,IERR)CALL MPI_ADDRESS(V,IV,IERR)CALL MPI_TYPE_EXTENT(MPI_REAL,EX,IERR)DISPS(1)=0DISPS(2)=IV-IUDISPS(3)=EXCALL MPI_TYPE_STRUCT(3,LENS,DISPS,TYPES,CTYPE,IERR)DISPS(3)=EX*NCALL MPI_TYPE_STRUCT(3,LENS,DISPS,TYPES,LTYPE,IERR)......! U(1:N,1),V(1:N,1)CALL MPI_SEND(U(1,1),N,CTYPE,...)! U(1,1:M),V(1,1:M)CALL MPI_SEND(U(1,1),M,LTYPE,...)! U(1:N,M),V(1:N,M)CALL MPI_SEND(U(1,M),N,CTYPE,...)! U(N,1:M),V(N,1:M)CALL MPI_SEND(U(N,1),M,LTYPE,...)......12§1.3.3MPI Get elementsMPI_Get_elements MPI_Get_count , MPI .MPI_Get_elements count MPI_UNDEFINED ,MPI_Get_count count .Cint MPI_Get_elements(MPI_Status*status,MPI_Datatype datatype,int*count)Fortran77MPI_GET_ELEMENTS(STATUS,DATATYPE,COUNT,IERR)INTEGER STATUS(MPI_STATUS_SIZE),DATATYPE,COUNT,IERR1.15MPI Get elements: 04-ex4.f§1.4MPI , MPI_PACKED, PVM , .§1.4.1Cint MPI_Pack(void*inbuf,int incount,MPI_Datatype datatype,void*outbuf,int outsize,int*position,MPI_Comm comm)Fortran77MPI_PACK(INBUF,INCOUNT,DATATYPE,OUTBUF,OUTSIZE,+POSITION,COMM,IERR)<type>INBUF(*),OUTBUF(*)INTEGER INCOUNT,DATATYPE,OUTSIZE,POSITION,COMM,+IERRinbuf incount datatype . outbuf .outsize outbuf ( , ).comm .position , MPI_Pack position 0, MPI_Pack , .MPI_Pack position .§1.4.2Cint MPI_Unpack(void*inbuf,int insize,int*position,13void*outbuf,int outcount,MPI_Datatype datatype,MPI_Comm comm)Fortran77MPI_UNPACK(INBUF,INSIZE,POSITION,OUTBUF,OUTCOUNT,+DATATYPE,COMM,IERR)<type>INBUF(*),OUTBUF(*)INTEGER INSIZE,POSITION,OUTCOUNT,DATATYPE,COMM,+IERRMPI_Unpack , MPI_Pack : inbuf outcount datatype outbuf . MPI_Pack , inbuf insize MPI_Pack outbuf outsize, outbuf outcount MPI_Pack inbuf incount.§1.4.3MPI , . MPI_Pack_size , .Cint MPI_Pack_size(int incount,MPI_Datatype datatype,MPI_Comm comm,int*size)Fortran77MPI_PACK_SIZE(INCOUNT,DATATYPE,COMM,SIZE,IERR)INTEGER INCOUNT,DATATYPE,COMM,SIZE,IERRsize incount datatype comm .1.16INTEGER*4BUFF(64)INTEGER N,MREAL A(5),B(5)INTEGER POSITION......IF(MYRANK.EQ.0)THENPOSITION=0CALL MPI_PACK(N,1,MPI_INTEGER,BUFF,256,POSITION,+MPI_COMM_WORLD,IERR)CALL MPI_PACK(M,1,MPI_INTEGER,BUFF,256,POSITION,+MPI_COMM_WORLD,IERR)CALL MPI_PACK(A,5,MPI_REAL,BUFF,256,POSITION,+MPI_COMM_WORLD,IERR)CALL MPI_PACK(B,5,MPI_REAL,BUFF,256,POSITION,+MPI_COMM_WORLD,IERR)CALL MPI_SEND(BUFF,256,MPI_PACKED,1,111,+MPI_COMM_WORLD,IERR)ELSE IF(MYRANK.EQ.1)THENCALL MPI_RECV(BUFF,256,MPI_PACKED,0,111,14+MPI_COMM_WORLD,IERR)POSITION=0CALL MPI_UNPACK(BUFF,256,POSITION,N,1,+MPI_INTEGER,MPI_COMM_WORLD,IERR)CALL MPI_UNPACK(BUFF,256,POSITION,M,1,+MPI_INTEGER,MPI_COMM_WORLD,IERR)CALL MPI_UNPACK(BUFF,256,POSITION,A,5,+MPI_REAL,MPI_COMM_WORLD,IERR)CALL MPI_UNPACK(BUFF,256,POSITION,B,5,+MPI_REAL,MPI_COMM_WORLD,IERR)ENDIF§1.5MPI1.1( MPI_Address,MPI_Type_hvector ),C Fortran77 : C MPI_Aint, Fortran77MPI_INTEGER.MPI_Aint MPI C , 64 int , MPI_Aint long, 32 MPI_Aint int. MPI_Aint MPI 32 64 .MPI1.1 Fortran77 , INTEGER. Fortran77 , 32 64 , Fortran77 2GB( 4GB) ., C , MPI_Type_size size int*, 2GB.MPI2 , MPI_Get_address,MPI_Type_create_hvector ,C Fortran(90) . MPI2.02.6 .15MPI (I/O) MPI2.0. MPI2.0 , C,C++, Fortran . ,Fortran Fortran90 (INTEGER(MPI_OFFSET_KIND)), Fortran77 ( INTEGER*4,INTEGER*8 ), .MPICH1.2.1 , MPI . , MPI . MPICH1.2.1, Fortran ’mpif.h’ ,’mpiof.h’.( MPICH1.2.0 , ).§2.1(file)MPI .MPI .MPI :MPI (MPI_File_open) , .(displacement) , .(etype) (elementary type) MPI . MPI , ( ) .MPI : (offset) , .(filetype) MPI , ., MPI . , .(view) . : , , .,MPI , .(offset)MPI I/O ( ) , .(file size) .16(file pointer) MPI ( ).MPI , , (individualfile pointer), , (sharedfilepointer).(file handle)MPI , , .MPI .2.1 ext(MPI_REAL)=4,etype=MPI_REAL, fh 4 p i,i=0,1,2,3, :p0:filetype={(REAL,0),(LB,0),(UB,16)}p1:filetype={(REAL,4),(LB,0),(UB,16)}p2:filetype={(REAL,8),(LB,0),(UB,16)}p3:filetype={(REAL,12),(LB,0),(UB,16)}0, :CALL MPI_FILE_READ(FH,A,1,MPI_REAL,STATUS,IERR)A.§2.2§2.2.1 MPICint MPI_File_open(MPI_Comm comm,char*filename,int amode,MPI_Info info,MPI_File*fh)FortranMPI_FILE_OPEN(COMM,FILENAME,AMODE,INFO,FH,IERR)CHARACTER*(*)FILENAMEINTEGER COMM,AMODE,INFO,FH,IERRMPI . , fh , .comm , comm .filename ,comm .amode ( ),comm amode .INFO MPI , MPI , . MPI_INFO_NULL , .amode .MPI :•MPI_MODE_RDONLY—17•MPI_MODE_RDWR—•MPI_MODE_WRONLY—•MPI_MODE_CREATE—•MPI_MODE_EXCL—•MPI_MODE_DELETE_ON_CLOSE—•MPI_MODE_UNIQUE_OPEN—•MPI_MODE_SEQUENTIAL—•MPI_MODE_APPEND—(C “|”,Fortran77 “+” , ).§2.2.2 MPICint MPI_File_close(MPI_File*fh)FortranMPI_FILE_CLOSE(FH,IERR)INTEGER FH,IERR. , ,fh MPI_FILE_NULL. .MPI_FILE_CLOSE , .§2.2.3Cint MPI_File_delete(char*filename,MPI_Info info)FortranMPI_FILE_DELETE(FILENAME,INFO,IERR)CHARACTER*(*)FILENAMEINTEGER INFO,IERR. , MPI_ERR_NO_SUCH_FILE . .§2.2.4Cint MPI_File_set_size(MPI_File fh,MPI_Offset size)FortranMPI_FILE_SET_SIZE(FH,SIZE,IERR)INTEGER FH,IERRINTEGER(KIND=MPI_OFFSET_KIND)SIZE( ) size. size, size . size, ,18.MPI_FILE_SET_SIZE , .§2.2.5Cint MPI_File_preallocate(MPI_File fh,MPI_Offset size)FortranMPI_FILE_PREALLOCATE(FH,SIZE,IERR)INTEGER FH,IERRINTEGER(KIND=MPI_OFFSET_KIND)SIZEsize, . size , .MPI_FILE_PREALLOCATE , .§2.2.6Cint MPI_File_get_size(MPI_File fh,MPI_Offset*size)FortranMPI_FILE_GET_SIZE(FH,SIZE,IERR)INTEGER FH,IERRINTEGER(KIND=MPI_OFFSET_KIND)SIZEsize .§2.3§2.3.1Cint MPI_File_get_group(MPI_File fh,MPI_Group*group)FortranMPI_FILE_GET_GROUP(FH,GROUP,IERR)INTEGER FH,GROUP,IERRgroup fh ( ) . .§2.3.2Cint MPI_File_get_amode(MPI_File fh,int*amode)FortranMPI_FILE_GET_AMODE(FH,AMODE,IERR)INTEGER FH,AMODE,IERR19amode fh .§2.4Cint MPI_File_set_view(MPI_File fh,MPI_Offset disp,MPI_Datatype etype,MPI_Datatype filetype,char*datarep,MPI_Info info)FortranMPI_FILE_SET_VIEW(FH,DISP,ETYPE,FILETYPE,DATAREP,+INFO,IERR)INTEGER FH,ETYPE,FILETYPE,INFO,IERRCHARACTER*(*)DATAREPINTEGER(KIND=MPI_OFFSET_KIND)DISPdisp( ), etype, filetype. datarep . info .MPI_FILE_SET_VIEW , .disp,filetype info , datarep etype .MPI_MODE_SEQUENTIAL , dispMPI_DISPLACEMENT_CURRENT( ).§2.4.1datarep , . :"native" .."internal" MPI , MPI .MPI , ."external32" IEEE ,external data representation( XDR).MPI ..MPI ( "native" )., MPI_REGISTER_DATAREP , . MPI .MPI , ., datarep "native" , (etype) (filetype) . , (portable datatype, §2.4.2,21 ), MPI MPI_FILE_SET_VIEW ( ) .20, , MPI_TYPE_LBMPI_TYPE_UB .§2.4.2MPI , , :MPI_TYPE_CONTIGUOUS,MPI_TYPE_VECTOR,MPI_TYPE_INDEXED,MPI_TYPE_DUP,MPI_TYPE_CREATE_SUBARRAY,MPI_TYPE_INDEXED_BLOCK,MPI_TYPE_CREATE_DARRAY( MPI2.0 ). , . , : MPI_TYPE_HINDEXED,MPI_TYPE_HVECTOR,MPI_TYPE_STRUCT( ).§2.4.3MPI ( ) ( "native" , ).Cint MPI_File_get_type_extent(MPI_File fh,MPI_Datatype datatype,MPI_Aint*extent)FortranMPI_FILE_GET_TYPE_EXTENT(FH,DATATYPE,EXTENT,IERR)INTEGER FH,DATATYPE,IERRINTEGER(KIND=MPI_ADDRESS_KIND)EXTENT§2.5MPI , xxxx READ WRITE, .21MPI_xxxx_AT MPI_xxxx_AT_ALLMPI_Ixxxx_AT MPI_xxxx_AT_ALL_BEGINMPI_xxxx_AT_ALL_ENDMPI_xxxx MPI_xxxx_ALLMPI_Ixxxx MPI_xxxx_ALL_BEGINMPI_xxxx_ALL_ENDMPI_xxxx_SHARED MPI_xxxx_ORDEREDMPI_Ixxxx_SHARED MPI_xxxx_ORDERED_BEGINMPI_xxxx_ORDERED_ENDMPI ( , ) . , , , ., . , , . , , . , , , .(noncollective) (collective) . , , , , . , , ., ,MPI (blocking) (nonblocking) (split) . , . , , ( ) MPI_WAIT MPI_TEST . (BEGIN) (END) , .§2.5.1(*_AT,*_AT_ALL) , MPI_FILE_READ_AT .Cint MPI_File_read_at(MPI_File fh,MPI_Offset offset,void*buf,int count,MPI_Datatype datatype,22MPI_Status*status)FortranMPI_FILE_READ_AT(FH,OFFSET,BUF,COUNT,DATATYPE,+STATUS,IERR)<type>BUF(*)INTEGER FH,COUNT,DATATYPE,STATUS(MPI_STATUS_SIZE),+IERRINTEGER(KIND=MPI_OFFSET_KIND)OFFSETfh ,offset .buf,count datatype . status ( ).§2.5.2, . offset , . , MPI_FILE_READ .Cint MPI_File_read(MPI_File fh,void*buf,int count,MPI_Datatype datatype,MPI_Status*status)FortranMPI_FILE_READ(FH,BUF,COUNT,DATATYPE,STATUS,IERR)<type>BUF(*)INTEGER FH,COUNT,DATATYPE,STATUS(MPI_STATUS_SIZE),+IERR§2.5.3. , MPI_FILE_READ_ORDERED .Cint MPI_File_read_ordered(MPI_File fh,void*buf,int count,MPI_Datatype datatype,MPI_Status*status)FortranMPI_FILE_READ_ORDERED(FH,BUF,COUNT,DATATYPE,STATUS,+IERR)<type>BUF(*)INTEGER FH,COUNT,DATATYPE,STATUS(MPI_STATUS_SIZE),+IERR, , ( ) ( ).23MPI_FILE_READ_SHARED MPI_FILE_WRITE_SHARED , , MPI_FILE_READ_ORDERED MPI_FILE_WRITE_ORDERED .§2.5.4, READ WRITE I , MPI_FILE_READ MPI_FILE_IREAD. status request, . , request , . , MPI_WAIT,MPI_TEST ., MPI_IREAD_AT .Cint MPI_File_iread_at(MPI_File fh,MPI_Offset offset,void*buf,int count,MPI_Datatype datatype,MPI_Request*request)FortranMPI_FILE_IREAD_AT(FH,OFFSET,BUF,COUNT,DATATYPE,+REQUEST,IERR)<type>BUF(*)INTEGER FH,COUNT,DATATYPE,REQUEST,IERRINTEGER(KIND=MPI_OFFSET_KIND)OFFSET§2.5.5MPI , _BEGIN _END . , , . :MPI_FILE_READ_AT_ALL_BEGIN(fh,offset,buf,count,datatype)MPI_FILE_READ_AT_ALL_END(fh,buf,status)MPI_FILE_WRITE_AT_ALL_BEGIN(fh,offset,buf,count,datatype)MPI_FILE_WRITE_AT_ALL_END(fh,buf,status)MPI_FILE_READ_ALL_BEGIN(fh,buf,count,datatype)MPI_FILE_READ_ALL_END(fh,buf,status)MPI_FILE_WRITE_ALL_BEGIN(fh,buf,count,datatype)MPI_FILE_WRITE_ALL_END(fh,buf,status)MPI_FILE_READ_ORDERED_BEGIN(fh,buf,count,datatype)24MPI_FILE_READ_ORDERED_END(fh,buf,status)MPI_FILE_WRITE_ORDERED_BEGIN(fh,buf,count,datatype)MPI_FILE_WRITE_ORDERED_END(fh,buf,status)§2.6§2.6.1§2.6.1.1Cint MPI_File_seek(MPI_File fh,MPI_Offset offset,int whence)FortranMPI_FILE_SEEK(FH,OFFSET,WHENCE,IERR)INTEGER FH,WHENCE,IERRINTEGER(KIND=MPI_OFFSET_KIND)OFFSET. whence :•MPI_SEEK_SET— offset•MPI_SEEK_CUR— offset•MPI_SEEK_END— offset§2.6.1.2Cint MPI_File_get_position(MPI_File fh,MPI_Offset*offset)FortranMPI_FILE_GET_POSITION(FH,OFFSET,IERR)INTEGER FH,IERRINTEGER(KIND=MPI_OFFSET_KIND)OFFSEToffset .§2.6.2§2.6.2.1Cint MPI_File_seek_shared(MPI_File fh,MPI_Offset offset,int whence)FortranMPI_FILE_SEEK_SHARED(FH,OFFSET,WHENCE,IERR)INTEGER FH,WHENCE,IERRINTEGER(KIND=MPI_OFFSET_KIND)OFFSET25. whence :•MPI_SEEK_SET— offset•MPI_SEEK_CUR— offset•MPI_SEEK_END— offsetMPI_FILE_SEEK_SHARED , .§2.6.2.2Cint MPI_File_get_position_shared(MPI_File fh,MPI_Offset*offset)FortranMPI_FILE_GET_POSITION_SHARED(FH,OFFSET,IERR)INTEGER FH,IERRINTEGER(KIND=MPI_OFFSET_KIND)OFFSEToffset .§2.6.3Cint MPI_File_get_byte_offset(MPI_File fh,MPI_Offset offset,MPI_Offset*disp)FortranMPI_FILE_GET_BYTE_OFFSET(FH,OFFSET,DISP,IERR)INTEGER FH,IERRINTEGER(KIND=MPI_OFFSET_KIND)OFFSET,DISPetype (offset) (disp).§2.7,MPI , , . , , .MPI (atomicity, ) . , , MPI_FILE_SYNC (MPI_BARRIER) .§2.7.1Cint MPI_File_set_atomicity(MPI_File fh,int flag)Fortran26MPI_FILE_SET_ATOMICITY(FH,FLAG,IERR)INTEGER FH,IERRLOGICAL FLAG. flag true ,MPI ( ) . flag false ,MPI , .MPI_FILE_SET_ATOMICITY , .2.2 .INTEGER STATUS(MPI_STATUS_SIZE),FH,A(10)......CALL MPI_FILE_OPEN(MPI_COMM_WORLD,’myfile’,+MPI_MODE_RDWR+MPI_MODE_CREATE,+MPI_INFO_NULL,FH,IERR)CALL MPI_FILE_SET_VIEW(FH,0,MPI_INTEGER,MPI_INTEGER,+’native’,MPI_INFO_NULL,IERR)CALL MPI_SET_ATOMICITY(FH,.TRUE.,IERR)IF(MYRANK.EQ.0)THENDO I=1,10A(I)=5ENDDOCALL MPI_FILE_WRITE_AT(FH,0,A,10,MPI_INTEGER,+STATUS,IERR)ELSE IF(MYRANK.EQ.1)THENCALL MPI_FILE_READ_AT(FH,0,A,10,MPI_INTEGER,+STATUS,IERR)ENDIF, atomicity true, 1 0 10 5. atomicity false, 1 , MPI .§2.7.2 atomicityCint MPI_File_get_atomicity(MPI_File fh,int*flag)FortranMPI_FILE_GET_ATOMICITY(FH,FLAG,IERR)INTEGER FH,IERRLOGICAL FLAGflag atomicity .§2.7.3Cint MPI_File_sync(MPI_File fh)27FortranMPI_FILE_SYNC(FH,IERR)INTEGER FH,IERR. , . ., , , MPI_FILE_SYNC, (MPI_BARRIER). MPI_FILE_SYNC , .MPI_FILE_SYNC , .§2.8Cint MPI_Type_create_subarray(int ndims,int array_of_sizes[],int array_of_subsizes[],int array_of_starts[],int order,MPI_Datatype oldtype,MPI_Datatype*newtype)FortranMPI_TYPE_CREATE_SUBARRAY(NDIMS,ARRAY_OF_SIZES,+ARRAY_OF_SUBSIZES,ARRAY_OF_STARTS,ORDER,+OLDTYPE,NEWTYPE,IERR)INTEGER NDIMS,ARRAY_OF_SIZES(*),ARRAY_OF_SUBSIZES(*),+ARRAY_OF_STARTS(*),ORDER,OLDTYPE,NEWTYPE,IERR, ., n ( ) n . , .ndims .array_of_sizes[i] i .array_of_subsizes[i] i .array_of_starts[i] i ( C Fortran 0 ). order ,order=MPI_ORDER_C C ,order=MPI_ORDER_FORTRAN Fortran .oldtype .newtype .0 . , , .oldtype , newtype .2.3 Jacobi :NSAVE( ) RST( ). NSAVE>0, NSAVE jacobi.dat ( NSAVE≤0 ). RST=.true. ,28jacobi.dat , RST=.false. 0 .MPI MPICH-1.2.1, jacobi5.cmm mpiof.h . 64 READ_DATA WRITE_DATA OFFSET INTEGER*8 INTEGER(KIND=MPI_OFFSET_KIND)(Fortran90/95).COMMON : jacobi5.cmmFortran77 : jacobi5.f29。