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


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

本篇文章进行说明解释

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

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

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

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

可以这样使用:

PstreamBuffers pBuffers(Pstream::commsTypes::nonBlocking);

for (label proci = 0; proci < Pstream::nProcs(); proci++)
{
if (proci != Pstream::myProcNo())
{
someObject vals;
UOPstream str(proci, pBuffers);
str << vals;
}
} pBuffers.finishedSends(); // no-op for blocking for (label proci = 0; proci < Pstream::nProcs(); proci++)
{
if (proci != Pstream::myProcNo())
{
UIPstream str(proci, pBuffers);
someObject vals(str);
}
}

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



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



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

打开Pstream类的源码:

点击查看代码
namespace Foam
{ /*---------------------------------------------------------------------------*\
Class Pstream Declaration
\*---------------------------------------------------------------------------*/ class Pstream
:
public UPstream
{ protected: // Protected data //- Transfer buffer
DynamicList<char> buf_; public: // Declare name of the class and its debug switch
ClassName("Pstream"); // Constructors //- Construct given optional buffer size
Pstream
(
const commsTypes commsType,
const label bufSize = 0
)
:
UPstream(commsType),
buf_(0)
{
if (bufSize)
{
buf_.setCapacity(bufSize + 2*sizeof(scalar) + 1);
}
} // Gather and scatter //- Gather data. Apply bop to combine Value
// from different processors
template<class T, class BinaryOp>
static void gather
(
const List<commsStruct>& comms,
T& Value,
const BinaryOp& bop,
const int tag,
const label comm
); //- Like above but switches between linear/tree communication
template<class T, class BinaryOp>
static void gather
(
T& Value,
const BinaryOp& bop,
const int tag = Pstream::msgType(),
const label comm = Pstream::worldComm
); //- Scatter data. Distribute without modification. Reverse of gather
template<class T>
static void scatter
(
const List<commsStruct>& comms,
T& Value,
const int tag,
const label comm
); //- Like above but switches between linear/tree communication
template<class T>
static void scatter
(
T& Value,
const int tag = Pstream::msgType(),
const label comm = Pstream::worldComm
); // Combine variants. Inplace combine values from processors.
// (Uses construct from Istream instead of <<) template<class T, class CombineOp>
static void combineGather
(
const List<commsStruct>& comms,
T& Value,
const CombineOp& cop,
const int tag,
const label comm
); //- Like above but switches between linear/tree communication
template<class T, class CombineOp>
static void combineGather
(
T& Value,
const CombineOp& cop,
const int tag = Pstream::msgType(),
const label comm = Pstream::worldComm
); //- Scatter data. Reverse of combineGather
template<class T>
static void combineScatter
(
const List<commsStruct>& comms,
T& Value,
const int tag,
const label comm
); //- Like above but switches between linear/tree communication
template<class T>
static void combineScatter
(
T& Value,
const int tag = Pstream::msgType(),
const label comm = Pstream::worldComm
); // Combine variants working on whole List at a time. template<class T, class CombineOp>
static void listCombineGather
(
const List<commsStruct>& comms,
List<T>& Value,
const CombineOp& cop,
const int tag,
const label comm
); //- Like above but switches between linear/tree communication
template<class T, class CombineOp>
static void listCombineGather
(
List<T>& Value,
const CombineOp& cop,
const int tag = Pstream::msgType(),
const label comm = Pstream::worldComm
); //- Scatter data. Reverse of combineGather
template<class T>
static void listCombineScatter
(
const List<commsStruct>& comms,
List<T>& Value,
const int tag,
const label comm
); //- Like above but switches between linear/tree communication
template<class T>
static void listCombineScatter
(
List<T>& Value,
const int tag = Pstream::msgType(),
const label comm = Pstream::worldComm
); // Combine variants working on whole map at a time. Container needs to
// have iterators and find() defined. template<class Container, class CombineOp>
static void mapCombineGather
(
const List<commsStruct>& comms,
Container& Values,
const CombineOp& cop,
const int tag,
const label comm
); //- Like above but switches between linear/tree communication
template<class Container, class CombineOp>
static void mapCombineGather
(
Container& Values,
const CombineOp& cop,
const int tag = Pstream::msgType(),
const label comm = UPstream::worldComm
); //- Scatter data. Reverse of combineGather
template<class Container>
static void mapCombineScatter
(
const List<commsStruct>& comms,
Container& Values,
const int tag,
const label comm
); //- Like above but switches between linear/tree communication
template<class Container>
static void mapCombineScatter
(
Container& Values,
const int tag = Pstream::msgType(),
const label comm = UPstream::worldComm
); // Gather/scatter keeping the individual processor data separate.
// Values is a List of size UPstream::nProcs() where
// Values[UPstream::myProcNo()] is the data for the current processor. //- Gather data but keep individual values separate
template<class T>
static void gatherList
(
const List<commsStruct>& comms,
List<T>& Values,
const int tag,
const label comm
); //- Like above but switches between linear/tree communication
template<class T>
static void gatherList
(
List<T>& Values,
const int tag = Pstream::msgType(),
const label comm = UPstream::worldComm
); //- Scatter data. Reverse of gatherList
template<class T>
static void scatterList
(
const List<commsStruct>& comms,
List<T>& Values,
const int tag,
const label comm
); //- Like above but switches between linear/tree communication
template<class T>
static void scatterList
(
List<T>& Values,
const int tag = Pstream::msgType(),
const label comm = UPstream::worldComm
); // Exchange //- Helper: exchange contiguous data. Sends sendData, receives into
// recvData. If block=true will wait for all transfers to finish.
template<class Container, class T>
static void exchange
(
const UList<Container>& sendData,
const labelUList& recvSizes,
List<Container>& recvData,
const int tag = UPstream::msgType(),
const label comm = UPstream::worldComm,
const bool block = true
); //- Helper: exchange sizes of sendData. sendData is the data per
// processor (in the communicator). Returns sizes of sendData
// on the sending processor.
template<class Container>
static void exchangeSizes
(
const Container& sendData,
labelList& sizes,
const label comm = UPstream::worldComm
); //- Exchange contiguous data. Sends sendData, receives into
// recvData. Determines sizes to receive.
// If block=true will wait for all transfers to finish.
template<class Container, class T>
static void exchange
(
const UList<Container>& sendData,
List<Container>& recvData,
const int tag = UPstream::msgType(),
const label comm = UPstream::worldComm,
const bool block = true
);
};

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

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


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

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

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

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

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

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

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

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

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

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

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

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

void Time::func(int testValue)
{
mystatic = testValue ;
}

或者在全局区这样写:

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()的第二个重载比较常用,

template<class T>
static void gatherList
(
List<T>& Values,
const int tag = Pstream::msgType(),
const label comm = UPstream::worldComm
);
template<class T>
static void scatterList
(
List<T>& Values,
const int tag = Pstream::msgType(),
const label comm = UPstream::worldComm
);

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

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

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

Pstream::scatterList()与之类似

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

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

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

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. warning: ‘setAxisX‘ is deprecated

    解决 将 chart->setAxisX(valueAxisX,lineSeries); 改为: chart->addSeries(lineSeries); chart->creat ...

  2. MIUI12解决安装charles抓包安装证书后还是提示证书不安全

    前言 我抓包这么长时间,这个问题我还是第一次遇到,导致我反复试验,明明安装证书还是提示不安全.我用新买的手机MIUI 12系统弄了半天 解决方案 首先下载证书这部分是一个坑,小米 MIUI 12系统下 ...

  3. 初次邂逅 EasyExcel

    前言 由于工作原因,有这种需求,就是把数据库中的数据导出成 Excel 表格,同时,也得支持人家用 Excel 表格导入数据到数据库.当前项目也是在用 EasyExcel,所以我不得不学啦! 以前学习 ...

  4. 《不一般的 DFT》阅读随笔

    感觉上前置知识是毛啸 16 年的论文? 我手头也有,到时候发现有 at 到的地方就插一嘴说一句 srds 先这篇是因为有纸质版的这篇 感觉上大篇幅在讲复杂度模数大小相关的做法. 1 引言 我这写个啥? ...

  5. Microsoft.IO.RecyclableMemoryStream源码解读

    一.RecyclableMemoryStreamManager 源码地址:https://github.com/Microsoft/Microsoft.IO.RecyclableMemoryStrea ...

  6. CLion和动态链接库

    目录 生成链接库 链接库的使用 生成链接库 创建一个library项目 在项目中写好自己的代码 cmakelist cmake_minimum_required(VERSION 3.21) proje ...

  7. 【每日一题】【归并排序/堆排序&虚拟头结点】148. 排序链表-211220/220217【出栈时不断容易产生环状链表!】

    给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 . 进阶: 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗? 方法1:归并排序+使用辅助函数 ...

  8. 基于训练和推理场景下的MindStudio高精度对比

    摘要:MindStudio提供精度比对功能,支持Vector比对能力. 本文分享自华为云社区<[MindStudio训练营第一季]MindStudio 高精度对比随笔>,作者:Tianyi ...

  9. axios 中get 和post传参

    axios中get和ppost传参的方式: params是添加到url的请求字符串中的,一般用于get请求. data是添加到请求体(body)中的, 一般用于post请求. 上面,只是一般情况. 其 ...

  10. JavaScript:变量:声明和赋值变量时,内存结构是什么样的?

    这里只是大概画出内存结构的模型图,方便理解当我们声明变量和赋值变量时,到底在干嘛. 如上图所示,a赋值一个对象{},b赋值字符串hello: 于是内存里划了三个区域给我们,一个存储我们声明的变量表,即 ...