关于mpi的理论知识以及编写程序来实现数据积分中的梯形积分法。

几乎所有人的第一个程序是从“hello,world”程序开始学习的

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

int main(int argc, char* argv[])
{
    int rank, numproces;
    int namelen;
    char processor_name[MPI_MAX_PROCESSOR_NAME];

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);//获得进程号
    MPI_Comm_size(MPI_COMM_WORLD, &numproces);//返回通信子的进程数

    MPI_Get_processor_name(processor_name, &namelen);
    fprintf(stderr, "hello world! process %d of %d on %s\n", rank, numproces, processor_name);
    MPI_Finalize();

    return 0;
}

上述代码中,第1行中的#include "mpi.h" 头文件必须包含,在VS2015下编译生成exe文件(生成在debug文件中),通过cmd命令,进入debug文件夹目录中,敲入:mpiexec –n 4 TestForMPI.exe。其中命令中-n 4 表示使用4个进程进行并行计算,具体结果如图所示:

开始理论知识

通过上述的例子,我们对MPI编写的并行计算有了一个初步的认识,但是我们还不知道如何去真正编写一个MPI的并行程序,这需要我们学习一定的理论知识。在上面的例子中,有几个函数对于初学MPI的人来说并不明白是什么意思,下面就从这些函数入手。

MPI_Init:告知MPI系统进行所有必要的初始化设置。它是写在启动MPI并行计算的最前面的。具体的语法结构为:

MPI_Init(
     int* argc_p,
     char*** argv_p
    );

参数argc_p和argv_p分别指向main函数中的指针参数,为了弄明白这部分,还得从main函数的参数说起:C语言规定main函数的参数只能有两个,习惯上这两个参数写为argc和argv。因此,main函数的函数头可写为: main (argc,argv)。C语言还规定argc(第一个形参)必须是整型变量,argv( 第二个形参)必须是指向字符串的指针数组。其中argc参数表示了命令行中参数的个数(注意:文件名本身也 算一个参数),argc的值是在输入命令行时由系统按实际参数的个数自动赋予的。例如有命令行为: C:">E6 24 BASIC dbase FORTRAN由于文件名E6 24本身也算一个参数,所以共有4个参数,因此argc取得的值为4。argv参数是字符串指针数组,其各元素值为命令行中各字符串(参数均按字符串处 理)的首地址。

然而在MPI_Init函数中,并不一定都需要设置argc_p和argv_p这两个参数的,不需要的时候,将它们设置为NULL即可。

通讯子(communicator):MPI_COMM_WORLD表示一组可以互相发送消息的进程集合。

MPI_Comm_rank:用来获取正在调用进程的通信子中的进程号的函数。

MPI_Comm_size:用来得到通信子的进程数的函数。

这两个函数的具体结构如下:

int MPIAPI MPI_Comm_rank(
    __in MPI_Comm comm,
    __out int* rank
    );

int MPIAPI MPI_Comm_size(
    __in MPI_Comm comm,
    __out int* size
    );

它们的第一个参数都传入通信子作为参数,第二参数都用传出参数分别把正在调用通信子的进程号和通信的个数。

MPI_Finalize:告知MPI系统MPI已经使用完毕。它总是放到做并行计算的功能块的最后面,在此函数之后就不能再出现任何有关MPI相关的东西了。

以上只是表达了作为一个MPI并行计算的基本结构,并没有真正涉及进程之间的通信,为了更好的进行并行,必然需要进程间的通信,下面介绍两个进程间通信的函数,它们就是MPI_Send和MPI_Recv,分别用于消息的发送和接收。

MPI_Send:阻塞型消息发送。其结构为:

int MPI_Send (void *buf, int count, MPI_Datatype datatype,int dest, int tag,MPI_Comm comm)

参数buf为发送缓冲区;count为发送的数据个数;datatype为发送的数据类型;dest为消息的目的地址(进程号),其取值范围为0到np-1间的整数(np代表通信器comm中的进程数) 或MPI_PROC_NULL;tag为消息标签,其取值范围为0到MPI_TAG_UB间的整数;comm为通信器。

MPI_Recv:阻塞型消息接收。

int MPI_Recv (void *buf, int count, MPI_Datatype datatype,int source, int tag, MPI_Comm comm,MPI_Status *status)

参数buf为接收缓冲区;count为数据个数,它是接收数据长度的上限,具体接收到的数据长度可通过调用MPI_Get_count函数得到;datatype为接收的数据类型;source为消息源地址(进程号),其取值范围为0到np-1 间的整数(np代表通信器comm 中的进程数),或MPI_ANY_SOURCE,或MPI_PROC_NULL;tag为消息标签,其取值范围为0到MPI_TAG_UB间的整数或MPI_ANY_TAG;comm为通信器;status返回接收状态。

MPI_Status:返回消息传递的完成情况。数据结构的相关变量的意义就比较多了,具体可以参数使用手册。

typedef struct {
... ...
int MPI_SOURCE;             /*消息源地址*/
int MPI_TAG;                /*消息标签*/
int MPI_ERROR;              /*错误码*/
... ...
} MPI_Status;

通过编写程序来实现数据积分中的梯形积分法。

梯形积分法的基本思想是:将x轴上的区间划分为n个等长的子区间。然后计算子区间的和。

假设子区间的端点为xi和xi+1,那么子区间的长度为:h=xi+1-xi。那么梯形的面积就为:

由于n个子区间是等分的,边界分别为xi=a和x=b,则:

这片区域的所有梯形的面积和为:

变换为:

因此,串行的程序代码就可以这样写:

h = (b - a) / h;
approx = (f(a) + f(b)) / 2;
for (int i = 1; i < n-1; i++)
{
    x_i = a + i*h;
    approx += f(x_i);
}
approx = h*approx;

通过对串行程序的分析,对于这个例子,我们可以识别出两种任务:第一种获取单个矩形区域的面积,另一种是计算这些区域的面积和。

假设求f(x)=x3将梯形划分为1024个子区域计算[0,3]区域内的积分。
#include "mpi.h"
#include <stdio.h>
#include <cmath>

double Trap(double left_endpt, double right_endpt, double trap_count, double base_len);
double f(double x);

int main(int argc, char* argv[])
{
    int my_rank = 0, comm_sz = 0, n = 1024, local_n = 0;
    double a = 0.0, b = 3.0, h = 0, local_a = 0, local_b = 0;
    double local_int = 0, total_int = 0;
    int source;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);

    h = (b - a) / n;       /*  h is the same for all processes  */
    local_n = n / comm_sz; /*  So is the number of trapezoids */

    local_a = a + my_rank*local_n*h;
    local_b = local_a + local_n*h;
    local_int = Trap(local_a, local_b, local_n, h);

    if (my_rank != 0)
    {
        MPI_Send(&local_int, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
    }
    else
    {
        total_int = local_int;
        for (source = 1; source < comm_sz; source++)
        {
            MPI_Recv(&local_int, 1, MPI_DOUBLE, source, 0,
                MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            total_int += local_int;
        }
    }

    if (my_rank == 0)
    {
        printf("With n = %d trapezoids, our estimate\n", n);
        printf("of the integral from %f to %f = %.15e\n", a, b, total_int);

    }
    MPI_Finalize();
    return 0;
}
//子区域的积分函数
double Trap(double left_endpt, double right_endpt, double trap_count, double base_len)
{
    double estimate = 0, x = 0;
    int i;

    estimate = (f(left_endpt) + f(right_endpt)) / 2.0;
    for (i = 1; i <= trap_count - 1; i++)
    {
        x = left_endpt + base_len;
        estimate += f(x);
    }
    estimate = estimate*base_len;
    return estimate;
}
//数学函数
double f(double x)
{
    return pow(x, 3);
}

上述代码中,运行结果:

这段程序代码中的意思是,通过输入的进程数,将1024个划分的子区间等分的分配到控制台输入的进程(100个)进行子任务求解,求解完成之后,1-99进程计算的结果值通过MPI_Send函数发送出去,而0号进程使用MPI_Recv函数接收汇总,将每个进程的结果求和,得到区间的积分值。

本次并行计算消息之间的通信如下图:

至此,我们已经能使用MPI_Send消息发送函数和MPI_Recv消息接收函数进行简单的并行程序计算了,但最后的求和都是用号进程去做。

原文地址:https://www.cnblogs.com/loufangcheng/p/10645093.html

时间: 2024-11-05 16:07:37

关于mpi的理论知识以及编写程序来实现数据积分中的梯形积分法。的相关文章

150131-编写程序,打印其输入文件中单词长度直方图

编写程序,打印其输入文件中单词长度直方图(横.竖).       1. 横直方图     1 #include<stdio.h> 2 main() 3 { 4 char c; 5 int i=0; 6 7 while((c=getchar())!=EOF) { 8 9 if(c>='a'&&c<='z'||c>='A'&&c<='Z') 10 { 11 printf("%c",c); 12 i++; 13 } 14 1

解读Unity中的CG编写Shader系列五——理论知识

经过前面的系列文章中的三个例子,尽管代码简单,但是我想应该还有些地方没有100%弄明白,我们现在得回过头来补充一些必备的数学.图形学知识 1.图形管道 第一个例子中我有提到顶点着色和片段着色在整个图形绘制过程中属于一个环节,整个过程叫做管道,这个管道的所有环节包括: 在整个管道中,只有顶点着色与片段着色是可编程的,顶点数据和帧缓存是具体的数据,剩下的环节是固定功能的环节,即不能用cg去编程的环节. 2.数据流 3.语义.特殊参数(uniform) 前文已经大量接触到语义,语义的存在意义可以理解为

解读Unity中的CG编写Shader系列五??理论知识

转自 http://www.itnose.net/detail/6098474.html 经过前面的系列文章中的三个例子,尽管代码简单,但是我想应该还有些地方没有100%弄明白,我们现在得回过头来补充一些必备的数学.图形学知识 1.图形管道 第一个例子中我有提到顶点着色和片段着色在整个图形绘制过程中属于一个环节,整个过程叫做管道,这个管道的所有环节包括: 在整个管道中,只有顶点着色与片段着色是可编程的,顶点数据和帧缓存是具体的数据,剩下的环节是固定功能的环节,即不能用cg去编程的环节. 2.数据

数据库入门理论知识介绍

数据库入门理论知识介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 前言: 1.目前90%以上的公司面临的运维的瓶颈都在后端 最常见的2大瓶颈就是: 1>.数据库(极难扩展): 2>.存储: 所以说做互联网的运维工程师要是把以上两点问题解决就可以轻松的搞定整个架构 2.什么是数据库: 查百度上有长篇大论了一下数据库特点,我们可以简单的理解: 数据库就是一个存放数据的仓库,这个仓库按照一定的数据结构(数据结构是指数据的组织形式或数据之间的联系)来组织,存储的,我们可以通过数

OSGI 理论知识

下面列出了主要的控制台命令: 表 1. Equinox OSGi 主要的控制台命令表 类别 命令 含义 控制框架 launch 启动框架 shutdown 停止框架 close 关闭.退出框架 exit 立即退出,相当于 System.exit init 卸载所有 bundle(前提是已经 shutdown) setprop 设置属性,在运行时进行 控制 bundle Install 安装 uninstall 卸载 Start 启动 Stop 停止 Refresh 刷新 Update 更新 展示

Web自动化测试理论知识

Web 自动化理论知识 1.自动化测试概述概念:用工具代替/辅助人工完成完成软件测试活动的过程特点:    可以对程序的新版本自动执行回归测试    可以执行一些手工测试困难或不可能进行的测试    可以更好地利用资源    测试具有一致性和可重复性误区:    期望自动化测试发现大量新故障    安全性错觉    自动化测试的维护开销 2.自动化测试的优势重复多次执行测试时节省很多时间提高测试覆盖率和测试精度实现自动化回归测试减少工作量减少手工测试人为产生的错误 提供规范化的过程和一致性更好的

精通并发与Netty入门一:Netty理论知识介绍

Netty是目前无论是国内还是国外各大互联网公司必备的一个网络应用框架.Netty本身既然是网络框架,处理的基本都是与网络相关的这样的一些作用.由于Netty本身在设计上的一些非常巧妙的方式,是对于NIO的一个很好的实现.Netty在各种应用场景下都会得到很广泛的应用.无论是传统的基于http的这种短连接方式还是基于底层Socket的这样的访问方式.另外还支持H5中规范中新增加的一个特别重要的标准,就是关于长连接的websocket这样一种新的规范.Netty对于其提供了非常好的支撑.那么Net

TestNG学习-001-基础理论知识

此 文主要讲述用 TestNG 的基础理论知识,TestNG 的特定,编写测试过程三步骤,与 JUnit4+ 的差异,以此使亲对 TestNG 测试框架能够有一个简单的认知. 希望能对初学 TestNG 测试框架的亲们有所帮助.若有不足之处,敬请大神指正,不胜感激! TestNG是什么?TestNG是一个灵感来自于 JUnit 和 NUnit 的一个设计用来简化广泛的测试需求的开源自动化测试框架,其引入了一些新的功能,使其功能更强大,而且易于使用,但是 TestNG 不是 JUnit 的扩展.它

堆和栈的理论知识

一.预备知识-程序的内存分配       1. 一个由c/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)-由编译器自动分配释放,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈.2.堆区(heap)-一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收.注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵.3.全局区(静态区)(static)-全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初