mpi进程通信间通信 数据地址空间

Help!!!进程间通信问题-如何传递数据给另一被自己创建的进程?
[问题点数:0分]
Help!!!进程间通信问题-如何传递数据给另一被自己创建的进程?
[问题点数:0分]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
匿名用户不能发表回复!|推荐这篇日记的豆列
&&&&&&&&&&&&基于数据分组方法的数据仓库并行预计算和查询(二)-计算机理论论文-论文联盟
您好,游客
背景颜色:
基于数据分组方法的数据仓库并行预计算和查询(二)
作者:陈云&
  MPI建立了一套有效的、可移植的、灵活的标准,并已经成为国际上最为广泛的并行程序设计平台。MPI可以使用于几乎所有的并行 计算 (共享存储和分布式存储、MPP、Cluster)和多个操作系统(UNIX、WindowsNT、Linux)。
  3.2&& MPI的特点与实现 &&& 如上一小节所述,MPI是一个消息传递模式下并行程序设计的标准规范。在标准的程序设计的基础上,加入实现进程间通信的MPI消息传递函数以及其他并行计算环境设置函数,就构成了MPI并行程序设计所依赖的并行编程环境。对于MPI的定义,需要理解以下三个方面[Du01]:
  (1) MPI是一个库而不是一门语言。MPI库可以被FORTRAN77/C/Fortran90/C++调用从语法上说它遵守所有对库函数/过程的调用规则和一般的函数/过程没有什么区别。
  (2) MPI是一种标准或规范的代表,而不特指某一个对它的具体实现。迄今为止所有的并行制造商都提供对MPI的支持,一个正确的MPI程序,可以不加修改地在所有并行机上运行。
  (3) MPI是一种消息传递编程模式并成为这种编程模式的代表和事实上的标准。MPI虽然很庞大,但是它的最终目的是服务于进程间通信这一目标的。
  由此可见,MPI是一个标准。就如同世界上其他标准一样,都会出现很多基于同一标准的不同产品,MPI也不例外。很多研究机构或者公司根据MPI的标准和自己的实际情况,编写出了不同的支持MPI程序的编程环境,而这些编程环境在MPI的世界里就被称为MPI实现。目前比较重要的MPI实现有以下两种:
  ●& MPICH[MPI07]。MPICH是一种最重要的MPI实现,是目前使用最广泛的免费MPI系统,大部分集群系统上的并行环境是MPICH。它由美国Argonne国家实验室和MSU共同进行维护。支持几乎所有Linux/UNIX以及Windows9x,NT,2000和XP系统。而且每当MPI推出新的版本时,就会有相应的MPICH实现版本。
  ●& LAM[LAM07]。由美国Ohio State University开发,主要用于异构的计算机
计算系统。
  3.3&& MPI编程的基本概念   一个MPI并行程序由一组进程或线程所组成。这些进程或线程可以运行在相同的机器上,也可以运行在不同的机器上。在MPI中,一个独立参与通信的个体被定义为一个进程。每个进程所运行的代码并不需要是同样的,进程间的通信是通过调用MPI通信原语来完成。在典型情况下,每个进程都是在自己特有的地址空间中运行,尽管有时在SMP上的MPI程序并不如此。MPI程序中的每个进程都有一个序号,用于在进程组(由MPI程序中部分或全部进程所构成的一个集合)中标识该进程,这个序号被称为进程号,取值范围由0开始。
  MPI程序中进程间通信是通过通信器(communicator)进行的,通信器提供了进程间通信的基本环境。MPI程序在启动时会自动创建两个通信器:MPI_COMM_WORLD和MPI_COMM_SELF。前者包含程序运行时的所有进程,后者则是由每个进程独自构成、仅包含自己的通信器。在MPI程序中,一个MPI进程由通信器和进程在该通信器中的进程号唯一标识,同一进程可以在不同通信器中有不同的进程号。进程可以通过调用MPI_Comm_rank函数来获得本进程在某指定通信器中的进程号。
  3.3.1 MPI的点对点通信   通信器使得进程间可以通过消息或同步操作来完成通信。消息指在进程间进行的一次数据交换,在MPI中,消息一般包含以下一些内容:通信器、源进程、目的进程、消息标签和数据。MPI进程中使用得最频繁,最基本的一种通信模式就是一对进程相互之间进行通信,也就是一个进程发送消息,另一个进程接收消息,这种通信方式在MPI中被称作点对点通信(point to point communication)。MPI有两大类型的点对点通信函数,一种称为阻塞式(blocking),另一种则是非阻塞式(unblocking)。
&&& ●& 阻塞式通信:阻塞式函数会等到通信操作实际完成,或者至少通信所涉及的数据已经被MPI环境处理好之后才会返回。如MPI_Send和MPI_Recv,分别是阻塞式的发送和接收函数。MPI_Send函数返回之后,表明消息已经发送完毕或者已经被MPI环境处理完毕,随后对于发送缓冲区的修改不会对已经发出的消息有所影响。而MPI_Recv函数返回后,表明消息已经接收完毕并且可以立即使用。
&&& ●& 非阻塞式通信:非阻塞式函数在调用后会立即返回,而实际的消息传递由MPI环境在后台执行。非阻塞式函数的命名是在阻塞式函数名的MPI_前缀之后加上一个“I”,如MPI_Isend和MPI_Irecv则是MPI_Send和MPI_Recv的对应非阻塞式通信版本。在调用非阻塞式函数之后,进程可以调用MPI_Wait函数来等待通信操作的完成,或者可以进行其他的计算工作,而不必将CPU时间浪费在通信上,但这时不能对相关的数据缓冲区进行操作。因为当前操作可能会与正在后台进行的通信发生冲突,产生错误使得程序出问题。要检测通信操作是否实际完成,应该调用MPI_Test函数来查询通信操作的完成情况。
  在MPI中,对于点对点通信,也存在着4种发送模式。这4种模式的对应函数名称不同,但参数表是一样的,它们之间的差异,存在于它们发送消息的方式和对接收方的状态要求的不同。这4种模式分别是:标准模式、缓冲模式、同步模式和就绪模式。
&&& ●& 标准模式:当消息长度小于或等于MPI环境预留的数据缓冲区大小时,MPI环境会将消息复制到缓冲区,然后立即返回。否则会当部分或全部消息发送完成后才返回。标准模式下,发送操作的完成需要与接收方联络。
&&& ●& 缓冲模式:MPI环境将消息复制到一个用户提供的缓冲区中,然后就立即返回,消息由MPI环境在后台执行。用户必须确保所提供的缓冲区能够容下将要发送的消息。缓冲模式下的发送操作不需要与接收方联络便可立即完成。
&&& ●&& 同步模式:同步模式是基于标准模式上,增加了一个要求。它要求确认接收方已经开始接收数据后函数调用才返回。
&&& ●&& 就绪模式:调用就绪模式发送时必须确保接收方已经正在等待接收该消息,不然就会产生错误。
  3.3.2 MPI程序结构 &&&&&& 下面是C/C++语言MPI程序的典型结构:
#include "mpi.h"
int main(int argc, char *argv[])
&&& int myrank,
&&& MPI_Init(&argc, &argv);
&&& MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
&&& MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
&&& ......
&&& MPI_Finalize();
&&& ......
&&& return 0;
表3.1&&&& MPI程序基本结构
  C/C++语言的MPI程序必须包含MPI的头文件mpi.h,以获得MPI函数的原型说明和MPI的预定义数据类型和常量。在使用C++作为MPI程序编程语言的时候,在编译程序时可能会遇到以下的出错信息:
“SEEK_SET is #defined but must not be for the C++ binding of MPI”
  这个问题是由于stdio.h和MPI C++接口同时都使用了SEEK_SET,SEEK_CUR,SEEK_END这些全局变量,这是MPI-2标准中的一个bug。要解决这个问题,一般会在#include “mpi.h”这句代码前加上以下三句:
#undef SEEK_SET
#undef SEEK_END
#undef SEEK_CUR
  MPI_Init函数用于初始化MPI系统环境。该函数应该在调用其他所有MPI函数之前(除了MPI_Initialized)调用,不然MPI环境还没建立,其他函数也无法运行。命令行参数argc和argv可以传递给MPI_Init,因为有时可以通过这些参数将运行进程的相关信息传递给MPI程序。一般来说,调用MPI_Init(0, 0)也是足够的了。
  函数MPI_Comm_size和MPI_Comm_rank分别返回指定通信器中的进程数目和本进程的进程号。在这个例子中,使用的通信器是MPI_COMM_WORLD,它包含了所有进程。
  MPI_Finalize函数是用来退出MPI系统环境的。调用它之后便不能再调用任何其他的MPI函数了。程序的主体运行部分一般是在MPI_Finalize之前。进程可以通过myrank变量判断自己是哪个进程来执行不同进程所应该做的工作。
  3.3.3 MPI编程的主从模式   构成并行程序的进程中有一个主进程(通常是进程0)其余为从进程。主进程与从进程的分工是不同的。主进程的工作一般负责整个并行程序的控制,分配数据和任务给从进程,从进程负责数据的处理和计算工作,同时主进程也可以参与数据的处理和计算工作。
  3.4&& 小结   MPI的一个最重要的特点就是免费和源代码开放,MPI可以被迅速接受和它为自己定下的高效率、方便移植和功能强大三个主要目标密不可分。它采用广为使用的语言FORTRAN和C/C++进行绑定也是它成功的一个重要因素,当然MPI的成功还因为它
和吸收了前期大量消息传递系统的。一个成功的标准是需要大量的和艰苦的努力的,MPI就是这种实践和努力的结果。
  第四章& 商立方体   联机分析处理(OLAP)由于要计算复杂的聚集函数,有很多的查询要从磁盘读取大量的数据,而OLAP的交互特性要求系统能快速地响应查询。为了解决这对矛盾,Gray等人提出了数据立方体(Data Cube)[GCB+97]。数据立方体概括了可能提出的所有的查询类型,并且将查询结果预先计算出来保存到磁盘。在响应查询时,通过查询重写把用户的查询转换为对某一个实例化视图的查询,极大地提高了查询响应速度。
  近年来,随着数据仓库应用的广泛,数据仓库的数据量也越来越大,使得数据立方体的数据量也相应地急剧增加。数据立方体存在一个明显的缺陷:由于需要计算多个聚集函数对于所有可聚合属性的集合,数据立方体需要大量的计算和巨大的磁盘存储空间,不能很好地适用于多维度的场合。因此,减少数据立方体所占用的空间成为了一个关键问题。对此,人们纷纷提出了多种数据立方体的数据压缩技术。其中一类是基于数据立方体单元间关系的压缩技术,它们利用这些关系,如上卷、下钻等,通过分析发现单元间能够去除掉的冗余信息。这样,在将这些冗余信息去除掉之后,数据立方体的存储空间得到压缩并且数据立方体元组之间的关系得以保留,这类技术是目前数据立方体压缩存储技术的主流,代表着未来数据立方体压缩存储技术的
  Wang等人提出了精简立方体(Condensed Cube)的概念[WLFY02],它其中有一个关键概念叫“Base Single Tuple(BST)”。它将具有相同BST的数据立方体单元归为同一类,仅仅存储BST和对应的单元集,其他不符合这些条件的元组则按原来的方式存储。通过去除相同BST的数据立方体单元间的冗余信息,精简立方体能够有效地减少数据立方体的数据量。
  Y. Sismanis等人提出了一种称为Dwarf的,基于语义压缩方法的立方体结构[SRD02]。它通过识别出立方体结构中具有相同前缀和后缀的语义信息,并去除这两种类型的冗余信息,可以十分显著地缩减数据立方体的存储空间。Dwarf在最好的情况下可以达到1/6000的压缩率。
  商立方体(Quotient Cube)的概念由Laks Lakshmanan等提出[LPH02],主要是为了解决立方体压缩过程中立方体单元间上卷和下钻逻辑关系的丢失问题。在商立方体中,所有的单元被划分为若干类,在同一类中的单元具有相同的聚集值。类的划分不仅仅是满足聚集值相同这个条件,同时也满足一些额外的条件。这些条件的限制,使得商立方体可以保留下数据立方体的语义结构。因此,每个类只需保存下某些能够代表本类中所有单元属性的单元,即可实现数据量的压缩。
  4.1&& 商立方体的分类 &&& 商立方体的主要思想是分类,它采用了一种单元分类方法,称为“覆盖”分类法[LPH02]。该方法与聚集函数类型无关,也就是说,数据立方体上的任一单元,无论它的度量值的聚集函数是哪种操作,这个单元都会被商立方体算法划分到同一类中。
  假设基表中的一条元组t,在数据立方体网格中,t能够通过某一存在的路径,上卷到单元c,则称c覆盖(cover)t。c的覆盖集(cover set)定义为:c所覆盖的所有基表中的元组。例如,在图2.1中,单元(*, B, M1)的覆盖集是{(GZ, B, M1), (SZ, B, M1)}。
  对于单元c和单元d,当c和d的覆盖集是相等的时候,它们被称为覆盖相等。例如,图2.1中的(*, B, M1)和(*, *, M1),它们的覆盖集都是{(GZ, B, M1), (SZ, B, M1)},因此这两个单元是覆盖相等的。
  商立方体的分类方法就是将覆盖相等的单元分为同一类。在同一类单元中,有个上界(upper bound)的概念。上界就是在该类中最小的元素,也就是说,上界是无法下钻到同类单元中其他任何一个单元的。使用覆盖相等方法产生的商立方体分类,每个类有且只有一个上界,且同类的单元具有相同的聚集值[LPZ03]。转贴于论文联盟 http://www.lwlm.com
欢迎浏览更多 →
相关文章 & & &
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款
论文写作指导、论文发表请咨询客服老师
内容分类导航
本栏目Digg排行
本栏目热门文章
本栏目最新更新第四章: 集合通信
4.5 收集(Gather)
MPI_GATHER(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root , comm)
 IN sendbuf
 发送消息缓冲区的起始地址(可变)
 IN sendcount  发送消息缓冲区中的数据个数(整型)
 IN sendtype 
发送消息缓冲区中的数据类型(句柄)
 OUT recvbuf   接收消息缓冲区的起始地址(可变,仅对于根进程)
 IN recvcount  待接收的元素个数(整型,仅对于根进程)
 IN recvtype   接收元素的数据类型(句柄,仅对于根进程)
 IN root   
接收进程的序列号(整型)
 IN comm      通信子(句柄)
int MPI_Gather(void* sendbuf, int sendcount, MPI_Datatype sendtype,
void* recvbuf, int recvcount, MPI_Datatype recvtype,
int root, MPI_Comm comm)
MPI_GATHER(SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNT, RECVTYPE,
ROOT, COMM, IERROR)
  &type& SENDBUF(*), RECVBUF(*)
  INTEGER SENDCOUNT, SENDTYPE, RECVCOUNT, RECVTYPE, ROOT, COMM, IERROR
每个进程(包括根进程)将其发送缓冲区中的内容发送到根进程,根进程根据发送这些数据的进程的序列号将它们依次存放到自已的消息缓冲区中.其结果就象一个组中的n个进程(包括根进程在内)都执行了一个调用:
  MPI_Send(sendbuf, sendcount, sendtype, root, ...),
同时根进程执行了n次调用:
  MPI_Recv(recvbuf+i*recvcount*extent(recvtype), recvcount, recvtype, i,...),
此处extent(recvtype)是调用函数MPI_Type_extent()所返回的类型,另外一种描述就象是组中的n个进程发送的n条消息按照它们的序列号连接起来,根进程通过调用MPI_RECV(recvbuf,
recvcount*n, recvtype,...) 来将结果消息接收过来.
对于所有非根进程,接收消息缓冲区被忽略.
一般来说,sendtype和recvtype都允许是派生类型,并且第i个进程的sendcount
和sendtype的类型必须和根进程中的recvcount和recvtype相同,这就隐含了在每个进程和根进程之间,发送的数据量必须和接收的数据量相等,但发送方和接收方之间的不同数据类型映射仍然是允许的.
此函数中的所有参数对根进程来说都是很重要的,而对于其他进程只有sendbuf、
sendcount、sendtype、root和comm是必不可少的.参数root和comm在所有进程中都必须是一致的.
对数据个数和类型的描述不应当导致根进程消息缓冲区的任何部分被重写,这样的调用是错误的.
注意在根进程中recvcount所表示的是它从每个进程中接收的消息条数, 而不是它所接收的全部消息条数.
MPI_GATHERV(sendbuf, sendcount, sendtype, recvbuf, recvcounts, displs,
recvtype, root, comm)
发送消息缓冲区的起始地址(可变)
发送消息缓冲区中的数据个数(整型)
发送消息缓冲区中的数据类型(句柄)
接收消息缓冲区的起始地址(可变,仅对于根进程)
recvcounts
整型数组(长度为组的大小), 其值为从每个进程接收
的数据个数(仅对于根进程)
整数数组,每个入口i表示相对于recvbuf的位移,此位
移处存放着从进程i中接收的输入数据(仅对于根进程)
接收消息缓冲区中数据类型(仅对于根进程)(句柄)
接收进程的序列号(句柄)
通信子(句柄)
int MPI_Gatherv(void* sendbuf, int sendcount, MPI_Datatype sendtype,
        void* recvbuf, int *recvcounts, int *displs,
     
MPI_Datatype recvtype, int root, MPI_Comm comm)
MPI_GATHERV(SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNTS,DISPLS,
   RECVTYPE, ROOT, COMM, IERROR)
  &type& SENDBUF(*), RECVBUF(*)
  INTEGER SENDCOUNT, SENDTYPE, RECVCOUNTS(*), DISPLS(*), RECVTYPE,
 ROOT, COMM, IERROR
MPI_GATHERV扩展了MPI_GATHER的功能, 它可以从不同的进程接收不同数量的数
据,因为此时recvcounts是一个数组,除此之外,它还提供了更大的灵活性,比如它提供了一个新的参数displs,用户可以将接收的数据存放到根进程消息缓冲区的任意处.
此函数调用的结果就象组中的每个进程(包括根进程在内)都向根进程发送一条消息:
MPI_Send(sendbuf, sendcount, sendtype, root,...),
同时根进程执行n次接收动作,
MPI_Recv(recvbuf+disp[i]*extent(recvtype),recvcounts[i],
recvtype,i, ...),
消息按其源进程中的序列号依次存放在根进程的消息接收缓冲区中,也就是说,第j个进程发送给根进程的数据将存放到根进程消息接收缓冲区recvbuf的第j部分,第j部分从recvbuf的偏移量为displs[j]处开始.
对于所有非根进程,接收消息缓冲区被忽略.
图4.2: 根进程从组中的每个其他进程收集100个整型数据
第i个进程的sendcount和sendtype的类型必须和根进程中的recvcounts[i]和recvtype相同,这意谓着在每个进程和根进程之间,发关的数据量必须和接收的数据量相等.但发送方和接收方之间的不同数据类型映射仍然是允许的,如例4.6所示.
此函数中的所有参数对根进程来说都是很重要的,而对于其他进程只有sendbuf、
sendcount、sendtype、root和comm是必不可少的.参数root和comm在所有进程中都必须是一致的.
对数据个数和类型的描述不应当导致根进程消息缓冲区的任何部分被重写,这样的调用是错误的.
4.5.1 应用MPI_GATHER和MPI_GATHERV的例子
例4.2: 从组中的每个进程收集100个整型数送给根进程.见.
int gsize,sendarray[100];
int root,*
MPI_Comm_size(comm,&gsize);
rbuf=(int *)malloc(gsize*100*sizeof(int));
MPI_Gather(sendarray,100,MPI_INT,rbuf,100,MPI_INT,root,comm);
例4.3: 例4.2的变更,仅对根进程分配消息接收缓冲区.
int gsize, sendarray[100];
int root, myrank, *
MPI_Comm_rank(comm, myrank);
if (myrank == root) {
MPI_Comm_size(comm, &gsize);
rbuf=(int *)malloc(gsize*100*sizeof(int));
MPI_Gather(sendarray, 100, MPI_INT, rbuf, 100, MPI_INT,
root, comm);
例4.4: 功能同例4.3,但使用了派生数据类型.注意此时数据类型已经不是整个集合中gsize*100个整型数了,因为在收集动作中提供了每个进程和根进程之间的类型匹配.
int gsize, sendarray[100];
int root, *
MPI_Comm_size(comm, &gsize);
MPI_Type_contiguous(100, MPI_INT, &rtype);
MPI_Type_commit(&rtype);
rbuf = (int *)malloc(gsize*100*sizeof(int));
MPI_Gather(sendarray, 100, MPI_INT, rbuf, 1, rtype,
root,comm);
例4.5 每个进程向根进程发送100个整型数,但在接收端设置每个集合(100个数据)的步长,用MPI_GATHERV函数和displs参数来实现,假设步长≥100.见
int gsize, sendarray[100];
int root, *rbuf,
int *displs, i, *
MPI_Comm_size(comm, &gsize);
rbuf = (int *)malloc(gsize*stride*sizeof(int));
displs = (int *)malloc(gsize*sizeof(int));
rcounts = (int *)malloc(gsize*sizeof(int));
for (i=0; i& ++i) {
displs[i] = i*
rcounts[i] = 100;
MPI_Gatherv(sendarray, 100, MPI_INT, rbuf, rcounts, displs, MPI_INT,
root, comm);
注意:如果步长&100时程序将出错.
图4.3: 根进程从组中的每个其他进程收集100个整型数据,每个数据集之间按一定步长分开存放
图4.4: 根进程从100*150的数组中收集第0列,每个数据集之间按一定步长分开存放
例4.6: 在接收方同例4.5,但发送的是每个100*150数组的第0列中的100个数据(以C语言为例)见.
int gsize, sendarray[100][150];
int root, *rbuf,
int *displs, i, *
MPI_Comm_size(comm, &gsize);
rbuf = (int *)malloc(gsize*stride*sizeof(int));
displs = (int *)malloc(gsize*sizeof(int));
rcounts = (int *)malloc(gsize*sizeof(int));
for (i=0; i& ++i)
displs[i] = i*
rcounts[i] = 100;
/* 为数组中的第一列数据生成相应的数据类型 */
MPI_Type_vector(100, 1, 150, MPI_INT, &stype);
MPI_Type_commit(&stype);
MPI_Gatherv(sendarray, 1, stype, rbuf, rcounts, displs, MPI_INT,
root, comm);
例4.7:进程i将100*150的整数数组中第i列的100-i个整数发送给根进程(以C语言为例)接收端和上述两例一样也设定步长.见
int gsize, sendarray[100][150], *
int root, *rbuf, stride,
int *displs, i, *
MPI_Comm_size(comm, &gsize);
MPI_Comm_rank(comm, &myrank);
rbuf = (int *)malloc(gsize*stride*sizeof(int));
displs = (int *)malloc(gsize*sizeof(int));
rcounts = (int *)malloc(gsize*sizeof(int));
for (i=0; i& ++i)
displs[i] = i*
rcounts[i] = 100-i;
/* 和上例不同处 */
/* 为要发送列的数据生成相应的数据类型 */
MPI_Type_vector(100-myrank, 1, 150, MPI_INT, &stype);
MPI_Type_commit(&stype);
/* sptr是&myrank&列的起始地址 */
sptr = &sendarray[0][myrank];
MPI_Gatherv(sptr, 1, stype, rbuf, rcounts, displs, MPI_INT,
root, comm);
注意:每个进程所接收的数据个数不同.
图4.5: 根进程从100*150的数组中收集第i列的100-i个整型数据,每个数据集之间按一定步长分开存放
例4.8:和例4.7一样,但发送端不同.为在发送端得到正确的步长,首先生成相应的步长,这样我们就可以读C数组中的某一列了,这同中的例3.32相类似.
int gsize,sendarray[100][150],*
int root,*rbuf,stride,myrank,disp[2],blocklen[2];
MPI_Datatype stype,type[2];
int *displs,i,*
MPI_Comm_size(comm, &gsize);
MPI_Comm_rank(comm, &myrank);
rubf = (int *)malloc(gsize*stride*sizeof(int));
displs = (int *)malloc(gsize*sizeof(int));
rcounts = (int *)malloc(gsize*sizeof(int));
for (i=0; i& ++i)
displs[i] = i*
rcounts[i] = 100-i;
/* 在整行范围内为一个整数生成数据类型 */
disp[0] = 0;
disp[1] = 150*sizeof(int);
type[0] = MPI_INT;
type[1] = MPI_UB;
blocklen[0] = 1;
blocklen[1] = 1;
MPI_Type_struct(2, blocklen, disp, type, &stype);
MPI_Type_commit(&stype);
sptr = &sendarray[0][myrank];
MPI_Gatherv(sptr, 100-myrank, stype, rbuf, rcounts, displs,
MPI_INT, root, comm);
例4.9: 在发送端同例4.7,但在接收端各数据块之间的步长是随之变化的.见.
int gsize,sendarray[100][150],*
int root,*rbuf,*stride,myrank,
int *displs,i,*rcounts,
MPI_Comm_size(comm, &gsize);
MPI_Comm_rank(comm, &myrank);
stride = (int *)malloc(gsize*sizeof(int));
/* 对stride[i]赋初值,i从0到gsize-1 */
/* 首先设置displs和rcounts向量 */
displs = (int *)malloc(gsize*sizeof(int));
rcounts = (int *)malloc(gsize*sizeof(int));
offset = 0;
for (i=0; i& ++i)
displs[i] =
offset += stride[i];
rcounts[i] = 100-i;
/* rbuf缓冲区的大小很容易得到 */
bufsize = displs[gsize-1]+rcounts[gsize-1];
rbuf = (int *)malloc(bufsize*sizeof(int));
/* 为将要发送的列生成相应的数据类型 */
MPI_Type_vector(100-myrank, 1, 150, MPI_INT, &stype);
MPI_Type_commit(&stype);
sptr = &sendarray[0][myrank];
MPI_Gatherv(sptr, 1, stype, rbuf, rcounts, displs, MPI_INT,
root, comm);
根进程从100*150的数组中收集第i列的100-i个整型数据,每个数据集之间按步长stride[i]分开存放
例4.10: 进程i从100*150数组的第i列开始发送num个整数(以C语言为例).这里比较困难的是根进程不知道变化的num的确切值,所以必须先收集各个num值,这些数据依次存放在接收端.
int gsize,sendarray[100][150],*
int root,*rbuf,stride,myrank,disp[2],blocklen[2];
MPI_Datatype stype,types[2];
int *displs,i,*rcounts,
MPI_Comm_size(comm, &gsize);
MPI_Comm_rank(comm, &myrank);
/* 首先根进程收集各个num的值 */
rcounts = (int *)malloc(gsize*sizeof(int));
MPI_Gather(&num, 1, MPI_INT, rcounts, 1, MPI_INT, root, comm);
/* 现在根进程已经得到正确的rcounts,这样我们可以设置displs[] 以便将这些
数据依次存放在接收端 */
displs = (int *)malloc(gsize*sizeof(int));
displs[0] = 0;
for (i=1; i& ++i)
displs[i] = displs[i-1]+rcounts[i-1];
/* 生成接收消息缓冲区 */
rbuf = (int *)malloc(gsize*(displs[gsize-1]+rcounts[gsize-1])
*sizeof(int));
/* 在整行范围内为一个整数生成数据类型 */
disp[0] = 0;
disp[1] = 150*sizeof(int);
type[0] = MPI_INT;
type[1] = MPI_UB;
blcklen[0] =1;
blcklen[1] = 1;
MPI_Type_struct(2, blocklen, disp, type, &stype);
MPI_Type_commit(&stype);
sptr = &sendarray[0][myrank];
MPI_Gatherv(sptr, num, stype, rbuf, rcounts, displs, MPI_INT,
root, comm);
Copyright: NPACT

我要回帖

更多关于 mpi 计算进程数 的文章

 

随机推荐