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


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. SqlServer 联合Update

    --1.创建一个备份表: select * into Users_Bak from Users --2.依据某个表进行更新: update Users_Bak set Users_Bak.Mobile ...

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

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

  3. JavaScript Promises, async/await

    new Promise() 的时候,传一个 executor 给 Promise. let promise = new Promise(function(resolve, reject) { // t ...

  4. day01-家具网购项目说明

    家具网购项目说明 1.项目前置技术 Java基础 正则表达式 Mysql JDBC 数据库连接池技术 满汉楼项目(包括框架图) JavaWeb 2.相关说明 这里先使用原生的servlet/过滤器,后 ...

  5. 粘包、struct模块、进程并行与并发

    目录 粘包现象 struct模块 粘包代码实战 udp协议(了解) 并发编程理论 多道技术 进程理论 进程并行与并发 进程的三状态 粘包现象 1.服务端连续执行三次recv 2.客户端连续执行三次se ...

  6. 一文聊透Apache Hudi的索引设计与应用

    Hudi索引在数据读和写的过程中都有应用.读的过程主要是查询引擎利用MetaDataTable使用索引进行Data Skipping以提高查找速度;写的过程主要应用在upsert写上,即利用索引查找该 ...

  7. History模式的配置细节

    旧文从语雀迁移过来,原日期:2021-09-13 前言 我们知道,vue 单页面应用打包出来是静态资源,一般需要 nginx 或者其他服务器访问:当如果 Vue Router 是采用 History ...

  8. 从Spring中学到的【2】--容器类

    容器类 我们在实际编码中,常常会遇到各种容器类,他们有时叫做POJO,有时又叫做DTO,VO, DO等,这些类只具有容器的作用,具有完全的get,set方法,作为信息载体,作数据传输用. 其实,很多地 ...

  9. 本机无法配置远程服务器上的MySQL ODBC连接驱动

    1.问题描述 我想要访问远程windows服务器上的MySQL数据库,因此需要在本地ODBC驱动上配好远程服务器的MySQL.但配置好基本信息后,测试的时候出现如下问题: 2.解决方法 之所以产生这种 ...

  10. win10 WSL2问题解决“WslRegisterDistribution failed with error: 0x800701bc”

    win10安装wsl过程报错信息如下: 造成该问题的原因是WSL版本由原来的WSL1升级到WSL2后,内核没有升级,前往微软WSL官网下载安装适用于 x64 计算机的最新 WSL2 Linux 内核更 ...