使用 C++ 模板的通用和元编程在多大程度上在计算科学中有用?
让我根据经验举一个例子。我每天使用的大多数库都以某种方式使用 OOP。OOP 能够隐藏许多领域所需的复杂性,它不是一种真正有助于提高性能的机制。可能发生的情况是库能够使用基于对象层次结构的特定优化,但大部分是关于向用户隐藏复杂性。查找设计模式,它们是通常用来完成这种复杂性隐藏的机制。
以 PETSc 为例。PETSc 使用 OOP 的检查器/执行器模型,其中任何算法查看给定对象中的可用例程,然后选择执行哪个来完成例程。这允许用户分离关注点,例如矩阵动作可以包括任何类型的阻塞或优化例程,并被众多迭代求解器有效使用。通过让用户能够指定他们自己的数据类型和评估,他们可以加快一些重要的例程,并且整个库的功能仍然可用。
我要举的另一个例子是 FEniCS 和 deal.II。这两个库都使用 OOP 来泛化大量的有限元方法。在元素类型、元素顺序、正交表示等所有内容中都是可以互换的。虽然这两个库都比一些特殊用途的结构化 FEM 代码“慢”,但它们能够解决用户不知道的 FEM 的许多复杂性的各种各样的问题。
我的最后一个例子是元素。Elemental 是一个新的密集线性代数库,它把管理 MPI 通信器和数据位置的困难变成了一个非常简单的语言结构。结果是,如果您有 FLAME 串行代码,通过更改数据类型,您还可以通过 Elemental 获得并行代码。更有趣的是,您可以通过将分布设置为另一个来玩数据分布。
OOP 应该被认为是一种管理复杂性的方法,而不是与手工组装竞争的范式。同样做得不好也会导致大量开销,因此必须保持时间并更新他们使用的机制。
我认为总的来说,模板元编程在实践中被发现是不可用的——它编译太慢,而且我们得到的错误信息根本无法破译。使用元编程时,新手的进入门槛也太高了。
当然,泛型编程是一个完全不同的问题,正如 Trilinos、deal.II(我自己的库)、DUNE 和许多其他库所见证的那样——表达对不同数据类型操作的相同概念是不费吹灰之力的,并且社区在很大程度上接受了它,只要它保持在避免元编程问题的范围内。我认为泛型编程显然是成功的。
当然,这些主题都没有立即与 OOP 相关联。再说一次,我想说的是,OOP 被科学计算界普遍接受。甚至不如泛型编程,它也不是一个争论的话题:过去 15 年中编写的每个成功的库(无论是用 C++、C 还是 Fortran 编写的)都使用 OOP 技术。
像科学计算这样的语言特性OOP
是使代码语句更紧凑,这有助于更好地理解和使用代码。例如,FFT
例程需要为每个函数调用携带大量参数,从而使代码变得繁琐。
通过使用module
orclass
语句,只能传递调用时需要的内容,因为其余参数与问题设置有关(即数组的大小和系数)。
根据我的经验,我有SUBROUTINE
55 个参数(输入和输出)的调用,我将其减少到 5 个以使代码更好。
这就是价值。
我是科学计算的通用编程和元编程的坚定拥护者。实际上,我正在为基于这些称为 Feel++ (http://www.feelpp.org) 的技术的 Galerkin 方法开发一个免费软件 C++ 库,该库正在稳步发展。诚然,仍然存在一些困难,例如编译时间慢,如果想要了解幕后发生的事情,学习曲线可能会很陡峭。然而,这是非常有趣和令人兴奋的。如果在库级别完成并隐藏特定领域语言背后的复杂性,您将获得一个非常强大的工具。我们可以使用非常广泛的方法来使用和比较。对于科学计算的教学目的,对于研究和新的数值方法,对于大规模应用,这太棒了,好吧,我们正在努力,但到目前为止一切都很好,我们已经可以做一些不错的事情了。我们有工程师、物理学家和数学家在使用它:他们中的大多数人只是将这种语言用于变分公式,他们对此很满意。看看我们的物理学家同事操纵的一些公式,我不希望看到它们“手工”完成,而没有高级语言来描述变分公式。我个人认为这些“技术”或“范式”现在对于解决科学计算代码的复杂性是必要的,因为必须将代码大小乘以一个巨大的因子。可能需要改进对 C++ 中元编程的支持,但它已经处于良好状态,尤其是自 C++11 以来。