对于数值仿真而言,无论是商软或者开源并行计算都是非常重要的,而且想把自身数值仿真能力提升一个层次,必须对并行计算有很好的理解与应用


openfoam并行通信主要通过Pstream类完成

本篇文章进行说明解释

Pstream类,类如其名,parallel_stream,并行计算时使用的信息流

类似的命名方法我们在c++文件读取时说过,std有fstream类读取写入文件/二进制文件,比如说我们要读取文件,会把读取内容放入缓存区内进行操作

  1. #include <iostream>
  2. #include <fstream> // ifstream类需要包含的头文件。
  3. #include <string> // getline()函数需要包含的头文件。
  4. using namespace std;
  5. int main()
  6. {
  7. string filename = R"(./test.txt)";
  8. //ifstream fin(filename, ios::in);
  9. ifstream fin;
  10. fin.open(filename , ios::in);
  11. // 判断打开文件是否成功。
  12. // 失败的原因主要有:1)目录不存在;2)文件不存在;3)没有权限,Linux平台下很常见。
  13. if (fin.is_open() == false)
  14. {
  15. cout << "打开文件" << filename << "失败。\n"; return 0;
  16. }
  17. string buffer;
  18. while (fin >> buffer)
  19. {
  20. cout << buffer << endl;
  21. }
  22. fin.close(); // 关闭文件,fin对象失效前会自动调用close()。
  23. cout << "操作文件完成。\n";
  24. }

类似的openfoam也有PstreamBuffers类进行并行通信缓冲

可以这样使用:

  1. PstreamBuffers pBuffers(Pstream::commsTypes::nonBlocking);
  2. for (label proci = 0; proci < Pstream::nProcs(); proci++)
  3. {
  4. if (proci != Pstream::myProcNo())
  5. {
  6. someObject vals;
  7. UOPstream str(proci, pBuffers);
  8. str << vals;
  9. }
  10. }
  11. pBuffers.finishedSends(); // no-op for blocking
  12. for (label proci = 0; proci < Pstream::nProcs(); proci++)
  13. {
  14. if (proci != Pstream::myProcNo())
  15. {
  16. UIPstream str(proci, pBuffers);
  17. someObject vals(str);
  18. }
  19. }

上面这个程序可以看到,先后使用UOPstream与UIPstream进行缓冲区的文件输出与读取,这就很像ofstream类与ifstream类,甚至命名方式上都有几分相似,我们打开相应的继承关系图



二者分别服务于IPstream类与OPstream类,我们再打开今天文章的主角,Pstream类继承关系图



发现IPstream类与OPstream类是Pstream类的衍生类,Pstream类是其基础

打开Pstream类的源码:

点击查看代码
  1. namespace Foam
  2. {
  3. /*---------------------------------------------------------------------------*\
  4. Class Pstream Declaration
  5. \*---------------------------------------------------------------------------*/
  6. class Pstream
  7. :
  8. public UPstream
  9. {
  10. protected:
  11. // Protected data
  12. //- Transfer buffer
  13. DynamicList<char> buf_;
  14. public:
  15. // Declare name of the class and its debug switch
  16. ClassName("Pstream");
  17. // Constructors
  18. //- Construct given optional buffer size
  19. Pstream
  20. (
  21. const commsTypes commsType,
  22. const label bufSize = 0
  23. )
  24. :
  25. UPstream(commsType),
  26. buf_(0)
  27. {
  28. if (bufSize)
  29. {
  30. buf_.setCapacity(bufSize + 2*sizeof(scalar) + 1);
  31. }
  32. }
  33. // Gather and scatter
  34. //- Gather data. Apply bop to combine Value
  35. // from different processors
  36. template<class T, class BinaryOp>
  37. static void gather
  38. (
  39. const List<commsStruct>& comms,
  40. T& Value,
  41. const BinaryOp& bop,
  42. const int tag,
  43. const label comm
  44. );
  45. //- Like above but switches between linear/tree communication
  46. template<class T, class BinaryOp>
  47. static void gather
  48. (
  49. T& Value,
  50. const BinaryOp& bop,
  51. const int tag = Pstream::msgType(),
  52. const label comm = Pstream::worldComm
  53. );
  54. //- Scatter data. Distribute without modification. Reverse of gather
  55. template<class T>
  56. static void scatter
  57. (
  58. const List<commsStruct>& comms,
  59. T& Value,
  60. const int tag,
  61. const label comm
  62. );
  63. //- Like above but switches between linear/tree communication
  64. template<class T>
  65. static void scatter
  66. (
  67. T& Value,
  68. const int tag = Pstream::msgType(),
  69. const label comm = Pstream::worldComm
  70. );
  71. // Combine variants. Inplace combine values from processors.
  72. // (Uses construct from Istream instead of <<)
  73. template<class T, class CombineOp>
  74. static void combineGather
  75. (
  76. const List<commsStruct>& comms,
  77. T& Value,
  78. const CombineOp& cop,
  79. const int tag,
  80. const label comm
  81. );
  82. //- Like above but switches between linear/tree communication
  83. template<class T, class CombineOp>
  84. static void combineGather
  85. (
  86. T& Value,
  87. const CombineOp& cop,
  88. const int tag = Pstream::msgType(),
  89. const label comm = Pstream::worldComm
  90. );
  91. //- Scatter data. Reverse of combineGather
  92. template<class T>
  93. static void combineScatter
  94. (
  95. const List<commsStruct>& comms,
  96. T& Value,
  97. const int tag,
  98. const label comm
  99. );
  100. //- Like above but switches between linear/tree communication
  101. template<class T>
  102. static void combineScatter
  103. (
  104. T& Value,
  105. const int tag = Pstream::msgType(),
  106. const label comm = Pstream::worldComm
  107. );
  108. // Combine variants working on whole List at a time.
  109. template<class T, class CombineOp>
  110. static void listCombineGather
  111. (
  112. const List<commsStruct>& comms,
  113. List<T>& Value,
  114. const CombineOp& cop,
  115. const int tag,
  116. const label comm
  117. );
  118. //- Like above but switches between linear/tree communication
  119. template<class T, class CombineOp>
  120. static void listCombineGather
  121. (
  122. List<T>& Value,
  123. const CombineOp& cop,
  124. const int tag = Pstream::msgType(),
  125. const label comm = Pstream::worldComm
  126. );
  127. //- Scatter data. Reverse of combineGather
  128. template<class T>
  129. static void listCombineScatter
  130. (
  131. const List<commsStruct>& comms,
  132. List<T>& Value,
  133. const int tag,
  134. const label comm
  135. );
  136. //- Like above but switches between linear/tree communication
  137. template<class T>
  138. static void listCombineScatter
  139. (
  140. List<T>& Value,
  141. const int tag = Pstream::msgType(),
  142. const label comm = Pstream::worldComm
  143. );
  144. // Combine variants working on whole map at a time. Container needs to
  145. // have iterators and find() defined.
  146. template<class Container, class CombineOp>
  147. static void mapCombineGather
  148. (
  149. const List<commsStruct>& comms,
  150. Container& Values,
  151. const CombineOp& cop,
  152. const int tag,
  153. const label comm
  154. );
  155. //- Like above but switches between linear/tree communication
  156. template<class Container, class CombineOp>
  157. static void mapCombineGather
  158. (
  159. Container& Values,
  160. const CombineOp& cop,
  161. const int tag = Pstream::msgType(),
  162. const label comm = UPstream::worldComm
  163. );
  164. //- Scatter data. Reverse of combineGather
  165. template<class Container>
  166. static void mapCombineScatter
  167. (
  168. const List<commsStruct>& comms,
  169. Container& Values,
  170. const int tag,
  171. const label comm
  172. );
  173. //- Like above but switches between linear/tree communication
  174. template<class Container>
  175. static void mapCombineScatter
  176. (
  177. Container& Values,
  178. const int tag = Pstream::msgType(),
  179. const label comm = UPstream::worldComm
  180. );
  181. // Gather/scatter keeping the individual processor data separate.
  182. // Values is a List of size UPstream::nProcs() where
  183. // Values[UPstream::myProcNo()] is the data for the current processor.
  184. //- Gather data but keep individual values separate
  185. template<class T>
  186. static void gatherList
  187. (
  188. const List<commsStruct>& comms,
  189. List<T>& Values,
  190. const int tag,
  191. const label comm
  192. );
  193. //- Like above but switches between linear/tree communication
  194. template<class T>
  195. static void gatherList
  196. (
  197. List<T>& Values,
  198. const int tag = Pstream::msgType(),
  199. const label comm = UPstream::worldComm
  200. );
  201. //- Scatter data. Reverse of gatherList
  202. template<class T>
  203. static void scatterList
  204. (
  205. const List<commsStruct>& comms,
  206. List<T>& Values,
  207. const int tag,
  208. const label comm
  209. );
  210. //- Like above but switches between linear/tree communication
  211. template<class T>
  212. static void scatterList
  213. (
  214. List<T>& Values,
  215. const int tag = Pstream::msgType(),
  216. const label comm = UPstream::worldComm
  217. );
  218. // Exchange
  219. //- Helper: exchange contiguous data. Sends sendData, receives into
  220. // recvData. If block=true will wait for all transfers to finish.
  221. template<class Container, class T>
  222. static void exchange
  223. (
  224. const UList<Container>& sendData,
  225. const labelUList& recvSizes,
  226. List<Container>& recvData,
  227. const int tag = UPstream::msgType(),
  228. const label comm = UPstream::worldComm,
  229. const bool block = true
  230. );
  231. //- Helper: exchange sizes of sendData. sendData is the data per
  232. // processor (in the communicator). Returns sizes of sendData
  233. // on the sending processor.
  234. template<class Container>
  235. static void exchangeSizes
  236. (
  237. const Container& sendData,
  238. labelList& sizes,
  239. const label comm = UPstream::worldComm
  240. );
  241. //- Exchange contiguous data. Sends sendData, receives into
  242. // recvData. Determines sizes to receive.
  243. // If block=true will wait for all transfers to finish.
  244. template<class Container, class T>
  245. static void exchange
  246. (
  247. const UList<Container>& sendData,
  248. List<Container>& recvData,
  249. const int tag = UPstream::msgType(),
  250. const label comm = UPstream::worldComm,
  251. const bool block = true
  252. );
  253. };

我们看到Pstream类有一个构造函数,剩下的都是静态成员函数,而这些成员函数就是并行通讯的工具箱

这里多问一句,为什么工具箱的函数都是静态成员函数


为什么这里用静态成员函数呢

用静态成员可以变量实现多个对象间的数据共享,比全局变量更安全

这里我详细说下,举个例子

  1. Time mytime1;
  2. mytime1.hour=2;
  3. Time mytime2;
  4. mytime2.hour=4;

这段程序中成员变量是跟着对象走的,他们的对象各自占用不同的内存地址,彼此互不影响

那我们想做类内的全局变量满足相互通信需求,在不同对象mytime1和mytime2中共享一个副本,怎么办

这时static关键字就派上用场了,增加了static关键字或成员函数不隶属整个对象,而隶属于整个类

因为这个变量跟着类走,所以调用时用“类名::成员变量名”或“类名::成员变量函数”进行调用(当然也可用“对象名.静态函数名”),表示明确的隶属关系,不创建对象也可进行访问编辑

在Pstream类调用工具箱中函数时,我们常见到这样的调用方式,而且不创建Pstream对象也可进行调用

  1. // 在head节点收集信息
  2. Pstream::gatherList(nInternalFaces);
  3. Pstream::gatherList(nBoundaries);

因为类的静态成员脱离了与对象的关系,普通成员变量的内存分配是在对象初始化时完成的,对于静态成员必须在程序的全局区进行清晰的初始化

全局区的初始化过程可由某个.cpp源文件的开头的静态成员函数完成,如下所示:

  1. void Time::func(int testValue)
  2. {
  3. mystatic = testValue ;
  4. }

或者在全局区这样写:

  1. int Time::mystatic=10;

这样能保证这个静态成员变量能够被正常使用。

此外静态成员函数只能调用静态成员变量,也没有this指针可以使用

这里上一张图可能更方便理解

C++程序运行时,静态变量和全局变量存储在数据段,所以需要在全局区通过直接分配内存或者静态函数进行分配内存

因而静态成员的生命周期与程序运行周期相同,在程序中只有一份,无论创建对象与否,或者创建多少对象

说到这里可能大家对Openfoam的并行通信多了一些理解,只要开始了并行计算那么就可以通过Pstream类内的成员函数进行通信调用,在同样的数据段副本上进行信息流沟通


接下来依次说下各个工具的使用

收发数据

Pstream::gather()与Pstream::scatter()分别有两个重载,分别是收集以及散布数据,不如后面Pstream::gatherList()与Pstream::scatterList()常用,这里不细说了

Pstream::combineGather()、Pstream::combineScatter()重载情况与上同,用于就地集中收集或散布的数据,不太常用

Pstream::listCombineGather()、Pstream::listCombineScatter()重载情况与上同,用于一次整合list容器中的变量

Pstream::mapCombineGather()、Pstream::mapCombineScatter()重载情况与上同,用于一次整合整个map容器中的变量

Pstream::gatherList()以及Pstream::scatterList()的第二个重载比较常用,

  1. template<class T>
  2. static void gatherList
  3. (
  4. List<T>& Values,
  5. const int tag = Pstream::msgType(),
  6. const label comm = UPstream::worldComm
  7. );
  8. template<class T>
  9. static void scatterList
  10. (
  11. List<T>& Values,
  12. const int tag = Pstream::msgType(),
  13. const label comm = UPstream::worldComm
  14. );

Pstream::gatherList()以及Pstream::scatterList()的输入第一个参数是Values

这个Values需要自己整合下,Values是UPstream::nProcs()数量大小的List,比如说我要收集内部面可以这样创建需要收集的List,

  1. List<label> nIternalFaces(Pstream::nProcs());
  2. nIternalFaces[Pstream::myProcNo()] = mesh.Cf().size();//比如说看看每个节点分到了多少网格
  3. Pstream::gatherList(nIternalFaces);//在头结点收集数据

Pstream::scatterList()与之类似

Pstream::gatherList()以及Pstream::scatterList()的输入第二个参数是Pstream::msgType(),默认为1,可以不输入

  1. int Foam::UPstream::msgType_(1);

Pstream::gatherList()以及Pstream::scatterList()的输入第三个参数是Pstream::msgType(),默认为0,可以不输入

  1. Foam::label Foam::UPstream::worldComm(0);

交换数据

Pstream::exchange()有两个重载,用于交换连续的数据,一般情况下等待其他所有传输完成再传输,可通过默认参数block()修改优先权

Pstream::exchangeSizes()用于交换数据的大小


下面是Pstream类函数相互关系


结语

并行开发远不止收发数据这么简单,还有很多类可说的,后续会一一进行介绍,并对openfoam并行计算进行优化

自从这个月15号找到人生支点后,基本上每天都在肝一篇说明书,逐渐开启这个季度的狂飙

一起探索openfoam也是相当有趣的一件事,非常欢迎私信讨论

指正的价值要比打赏更重要,下面是个人联系方式,能结交到志同道合的朋友是我的荣幸

Openfoam Pstream类探索的更多相关文章

  1. java enum类探索

    参考网址1, 参考网址2 一直对枚举有点迷惑,现在试着理解枚举. 1.首先,普通类与枚举 的区别.拿两个例子比较吧 普通类: /** * 一个普通类 * @author Administrator * ...

  2. Java类的继承与多态特性-入门笔记

    相信对于继承和多态的概念性我就不在怎么解释啦!不管你是.Net还是Java面向对象编程都是比不缺少一堂课~~Net如此Java亦也有同样的思想成分包含其中. 继承,多态,封装是Java面向对象的3大特 ...

  3. Phonebook 导出联系人到SD卡(.vcf)

    2014-01-13 16:53:55 1. 在Phonebook中导出联系人到内部存储,SD卡或者通过蓝牙.彩信.邮件等分享联系人时,通常会先将选择的联系人打包生成.vcf文件,然后将.vcf文件分 ...

  4. 浅析Linux中的进程调度

    2016-11-22 前面在看软中断的时候,牵扯到不少进程调度的知识,这方面自己确实一直不怎么了解,就趁这个机会好好学习下. 现代的操作系统都是多任务的操作系统,尽管随着科技的发展,硬件的处理器核心越 ...

  5. 创业之前 ——Paul Graham 最新博文

    原文:Paul Graham 译者:李智维 /LeanCloudproject师 2014年10月 (这篇文章是我在斯坦福大学举办的Sam Altman创业课堂上的嘉宾演讲稿.本意是写给大学生的,但当 ...

  6. Java核心知识体系4:AOP原理和切面应用

    1 概述 我们所说的Aop(即面向切面编程),即面向接口,也面向方法,在基于IOC的基础上实现. Aop最大的特点是对指定的方法进行拦截并增强,这种增强的方式不需要业务代码进行调整,无需侵入到业务代码 ...

  7. 探索Win32系统之窗口类(转载)

    Window Classes in Win32 摘要 本文主要介绍win32系统里窗口类的运做和使用机制,探索一些细节问题,使win32窗口类的信息更加明朗化. 在本文中,"类", ...

  8. 24小时学通Linux内核--内核探索工具类

    寒假闲下来了,可以尽情的做自己喜欢的事情,专心待在实验室里燥起来了,因为大二的时候接触过Linux,只是关于内核方面确实是不好懂,所以十天的时间里还是希望能够补充一下Linux内核相关知识,接下来继续 ...

  9. 关于虚拟继承类的大小问题探索,VC++ 和 G++ 结果是有区别的

    昨天笔试遇到个 关于类占用的空间大小的问题,以前没怎么重视,回来做个试验,还真发现了问题,以后各位笔试考官门,出题时请注明是用什么编译器. vc6/vc8 cl 和 Dev-C 的g++ 来做的测试: ...

  10. 十天学Linux内核之第一天---内核探索工具类

    原文:十天学Linux内核之第一天---内核探索工具类 寒假闲下来了,可以尽情的做自己喜欢的事情,专心待在实验室里燥起来了,因为大二的时候接触过Linux,只是关于内核方面确实是不好懂,所以十天的时间 ...

随机推荐

  1. uni-ajax使用示例

    官网 基于 Promise 的轻量级 uni-app 网络请求库 uni-ajax官网:https://uniajax.ponjs.com 安装 插件市场 在 插件市场 右上角选择 使用 HBuild ...

  2. 实践案例:平安健康的 Dubbo3 迁移历程总结

    本篇是 Apache Dubbo 的实践案例.感兴趣的朋友可以访问官网了解更多详情,或搜索关注官方微信公众号 Apache Dubbo 跟进最新动态. 1 背景 我们公司从15年开始就使⽤dubbo作 ...

  3. Datawhale组队学习_Task02:详读西瓜书+南瓜书第3章

    第3章 线性模型 家人们又来吃瓜了! 3.1 基本形式 线性模型的本质是通过一个所有属性的线性组合进行预测的函数,即 $\mathcal{f(x)=w_1x_1+w_2x_2+...+w_dx_d+b ...

  4. 4.11:Storm之WordCount

    〇.概述 1.拓扑结构 2.目标 使用storm进行计数实验. 一.启动服务   在网页中输入:http://localhost:8081可以查看storm的相关信息. 二.kafka操作 终端中输入 ...

  5. 【消息队列面试】15-17:高性能和高吞吐、pull和push、各种MQ的区别

    十五.kafka高性能.高吞吐的原因 1.应用 日志收集(高频率.数据量大) 2.如何保证 (1)磁盘的顺序读写-pagecache关联 rabbitmq基于内存读写,而kafka基于磁盘读写,但却拥 ...

  6. Java 中的接口还可以这样用,你知道吗?

    Java 程序员都知道要面向接口编程,那 Java 中的接口除了定义接口方法之外还能怎么用你知道吗?今天阿粉就来带大家看一下 Java 中的接口还可以有哪些用法. 基本特性 我们先看一下接口的基本特性 ...

  7. MassTransit 知多少 | 基于MassTransit Courier实现Saga 编排式分布式事务

    Saga 模式 Saga 最初出现在1987年Hector Garcaa-Molrna & Kenneth Salem发表的一篇名为<Sagas>的论文里.其核心思想是将长事务拆分 ...

  8. S2-008

    漏洞名称 S2-008(CVE-2012-0392) 远程代码执行漏洞 利用条件 Struts 2.0.0 - Struts 2.3.17 漏洞原理 S2-008 涉及多个漏洞,Cookie 拦截器错 ...

  9. 防微杜渐,未雨绸缪,百度网盘(百度云盘)接口API自动化备份上传以及开源发布,基于Golang1.18

    奉行长期主义的开发者都有一个共识:对于服务器来说,数据备份非常重要,因为服务器上的数据通常是无价的,如果丢失了这些数据,可能会导致严重的后果,伴随云时代的发展,备份技术也让千行百业看到了其" ...

  10. 数位排序【第十三届蓝桥杯省赛C++C组】

    数位排序 小蓝对一个数的数位之和很感兴趣,今天他要按照数位之和给数排序. 当两个数各个数位之和不同时,将数位和较小的排在前面,当数位之和相等时,将数值小的排在前面. 例如,\(2022\) 排在 \( ...