OOP 在并行计算中是否可以轻松高效地工作?

计算科学 matlab 并行计算
2021-12-20 21:14:27

我正在从事一项适合面向对象 (OO) 方法的任务。我在 MATLAB 中做这件事,因为我处于原型设计阶段。但是,我知道以后我肯定必须执行更大规模的计算(我的大学有一个集群)。方法是对计算量大的部分使用 mex 函数。但它不适用于 MATLAB 类。另一种选择是用C++编写代码,其OOP与MATLAB的OOP不同,但有很多相似之处,思路是一样的。将 OpenMP、MPI、PETSC 等与 C++ 类一起使用来并行化我的代码有多容易?第三种选择是忽略 OOP,但是我牺牲了程序的优雅性和可扩展性。我的问题:

1)您建议我继续使用 OOP,还是切换到程序化方式?

2) 您推荐我使用哪种并行化技术(OpenMP、MPI、PETSC 等)?我不想在上面投入大量时间。我对 MATLAB 非常熟练,但对 C 语言只有基本知识,对 C++ 一无所知。

编辑

从其中一条评论中可以看出,如果我使用标准变量或对象没有区别。所以重新制定问题1)

1)当我要进行并行化时,是否确定 OOP 不会让我的生活变得更艰难?我将创建一个特定的应用程序而不是通用工具;在这种情况下,有多少 OO C++ 难学?我不需要特殊的数据结构,只需要循环、if 语句和并行化库的调用。这是一个可行的解决方案,只是使类方法并行(以便实现对外部隐藏)还是需要完全重写?

3个回答

根据经验,您关于“大多数数字库'不喜欢' OOP,这就是为什么许多软件是用 C 或 Fortran 编写的”的评论是不正确的。相反,我会说过去 20 年来编写的几乎所有软件库都以某种方式或另一种方式面向对象。例如,PETSc 是面向对象的(尽管它是用 C 编写的,但它基本上在底层使用类、继承和虚函数),另一个大型线性代数库 Trilinos(用 C++ 编写)也是如此。在有限元上下文中,libMesh、FEniCS/Dolphin 和 deal.II(我自己的项目)都是用 C++ 以面向对象的方式编写的。事实上,甚至 MPI 也是以面向对象的方式编写的,其中通信器就像类,大多数函数都在它们上运行(在 C++ 中,人们会将它们写为成员函数)。

所以在现实中,不管是什么语言,面向对象的编程范式早已在科学计算领域取得了胜利。无论您是用 C++ 还是 C/Fortran 编写代码,这都是正确的——基本上所有重要的代码都使用 OOP 范式。

在不了解您的问题的情况下,让我说一些一般性的想法。

您绝对可以使用 openMP 或 MPI 并行化 OOP 代码。事实上,基本上所有解决计算科学问题的专业/商业软件都利用了 OOP 范式,因为 OOP 在封装、可扩展性、可维护性、模块化、继承等方面具有显着优势。我应该强调,openMP 和 MPI 是根本不同的并行化范式:

开放式MP

  1. 共享内存范式
  2. 对于加速给定处理器上的代码很有用。例如,您可以使用所有 4 个内核,而不是在 Quad Care 处理器上仅使用 4 个内核中的 1 个。
  3. 你所有的线程都能看到一切——即所有内核都可以读取写入相同的数组、向量等

MPI

  1. 分布式内存范式
  2. 有助于分解您的问题并将其分布在集群中的多台机器上。
  3. 每个单独的节点就像一台单独的计算机。您的问题必须分解并交给集群中的每个节点/计算机。然后该节点仅处理该数据。
  4. 如果您需要访问不同节点上的数据,则必须通过节点之间的通信调用来实现。
  5. 通信相对昂贵,应尽量减少。这与必须分解问题的事实相结合,意味着通常必须完全重新考虑顺序算法才能在分布式环境中工作。

杂交种

  1. 这里我们同时使用 MPI 和 openMP。例如,您可能会分解您的问题并将其分布在集群上的许多节点上。每个节点可能有 4、6、8、16 个或更多核心。然后在每个节点上使用 openMP 来获得进一步的加速。

我想说的是,一般来说并行化并不是一件容易的事——尤其是使用 MPI。很多时候,您需要完全重新考虑您的算法,以便它们在分布式环境中有效地工作。因此,我认为在短时间内同时学习 C++ 和 MPI 将非常具有挑战性。仅 C++ 就可能需要数年才能真正掌握。另一方面,从句法的角度来看,MPI 并不难——您可能只需要知道 10-15 个子例程调用——但在调整算法以处理分布式内存模型方面可能很困难。

对于初学者来说,openMP 可能是最简单的并且不太可能需要基本算法更改。你有一个缓慢的 for 循环?您可以使用简单的#pragma 语句加快这部分代码的速度。

结论

如果您的代码很大并且您希望它能够随着时间的推移而增长和维护,我肯定会推荐使用 OOP。哎呀,即使您的代码很小,OOP 也非常适合创建设计良好的模块化代码。学习像 C++ 这样的语言——特别是因为你已经知道 C——不会太难(但要成为专家需要时间),而且从长远来看它会给你带来好处。如果您认真对待自己的代码并行化,那么您最终会想要同时学习 openMP 和 MPI。同时,openMP 可能是最简单的,如果您使用 gcc,则不需要安装,因为它通过调用预处理器标志进行操作。

我认为 Python 与您提到的语言一起也值得考虑,因为它在快速开发方面比 Matlab 有几个优势,但使您免受使用 C/C++ 带来的许多低级细节的影响。

Python 中的科学计算生态系统非常丰富。您提到了 PETSc,其中有Python 绑定Python也有MPI 绑定这甚至还没有触及你可以用 Cython、Numba 等做的事情的表面。至于有限元方法,fenics相当流行,尽管绝不是唯一的选择。我在几个月内学会了 Python,但在使用了大约 4 年之后,我才觉得自己能够胜任 C++。

Python 在速度方面无法击败优化的 C/C++ 代码,但使用 Numba 和 Cython 等工具以及将计算密集型代码外包给 PETSc 和 Trilinos 时,性能差距可能只有 10-40% 。根据我的经验,在使用 C/C++ 编程时,您不得不担心内存管理和其他低级细节,如果您只对算法感兴趣,这可能不是您的最佳时间利用方式。

也就是说,学习 Python 并不妨碍你学习 C++;恰恰相反,你知道的语言越多,学习新语言就越容易。一个正在实践的计算科学家必须了解 C、C++、Python,可能还需要了解 Fortran 和 x86-64 汇编。我碰巧也喜欢 Lisp 和 OCaml,但这些只是为了好玩。

至于面向对象编程,原则上,使用这种范式不应妨碍大型科学应用程序的可扩展性或速度。使用 OOP 时的主要性能问题是虚函数调度的附加成本。谈论任何细节都需要很长时间,但可以说与在典型科学代码中执行的其他操作(例如线性代数操作)相比,这个成本通常完全可以忽略不计。另一方面,对开发人员的好处是巨大的。如果有的话,好好利用 OO 设计原则可以帮助您更轻松地编写并行代码。