使用C ++模板的泛型和元编程在计算科学上有多大用处?


17

C ++语言通过模板提供通用的编程元编程。这些技术已进入许多大型科学计算程序包(例如MPQCLAMMPSCGALTrilinos)。但是,它们在整体开发时间和可用性上是否具有相等或充分的效率,它们实际上对科学计算做出了哪些贡献,这些价值超出了诸如C或Fortran之类的非通用,非元语言。

给定科学计算任务,通过C ++模板进行的通用和元编程是否已证明通过任何易于理解的基准(代码行,人工工作等)衡量的生产率,表达力或可用性方面的改进?相应地,将C ++模板用于通用编程和元编程会带来哪些风险?


我担心这个问题可能太开放了,但是我想看看社区中人们怎么说。
Geoff Oxberry 2012年

1
我完全删除了评论出轨。如果您希望将其重新发布为聊天或meta,我很乐意这样做。在这里查看我的meta 。
阿隆·艾玛迪亚

3
另外,请参阅此处的相关问题,以获取有关何时使用以及何时避免使用表达式模板的建议
阿隆·艾玛迪亚

我认为说LAMMPS使用模板或元编程是不正确的。LAMMPS是面向对象的代码,大多数时候看起来都非常像Fortran。我也不认为MPQC有太多模板,但是它很大程度上是面向对象的和多态的。
杰夫

1
OpenFOAM大量使用模板和C ++的其他功能。
恩·乔

Answers:


14

我认为,总体上来说,已经发现模板元编程在实践中不可用-编译速度太慢,而且我们得到的错误消息也无法破译。使用元编程时,新手进入的障碍也太高了。

当然,正如Trilinos,deal.II(我自己的库),DUNE和许多其他库所见证的那样,泛型编程是一个完全不同的问题,表达对不同数据类型操作的相同概念是不费吹灰之力的,只要它处于避免元编程问题的范围之内,社区就已经接受了它。我认为通用程序编程显然是成功的。

当然,这些主题都没有立即与OOP相关。再说一次,OOP是科学计算界普遍接受的。甚至比通用编程还少,这不是辩论的话题:过去15年中编写的每个成功的库(无论是用C ++,C还是Fortran编写)都使用OOP技术。


4
对于初学者来说,tmp可能很困难,但是在库中通常做得很好。它是可以真正减少代码量的那些技术之一,但是您确实需要知道自己在做什么。如果您不相信我,请阅读Eigen或Elemental的资料。没有模板的漂亮代码几乎是不可能的。
aterrel 2012年

5
当然,这是一种有价值的技术。但是它很难维护,而且如果暴露于外部接口,通常也很难使用。就是说,我认为TMP尚未完全成为人们最初期望的成功的原因之一就是编译器变得非常好。TMP的事实是,在编译时就已经知道了很多事情,然后可以将它们作为常量传播到实际代码中。但是编译器在内联,函数克隆等方面已经变得非常擅长,因此,如今可以使用“常规”编程来获得很大一部分好处。
Wolfgang Bangerth 2012年

15

让我举一个基于经验的例子。我每天使用的大多数库都以某种方式使用OOP。OOP能够隐藏许多域所需的复杂性,它并不是真正有助于提高性能的机制。可能发生的事情是,一个库能够基于对象层次结构使用特定的优化,但是大多数情况下是向用户隐藏了复杂性。查找设计模式,它们是通常用来完成这种复杂性隐藏的机制。

以PETSc为例。PETSc使用OOP的检查器/执行器模型,其中的任何算法都会查看给定对象中的可用例程,并选择要执行的例程以完成例程。这允许用户分离问题,例如,矩阵动作可以包括任何类型的阻塞或优化例程,并且可以被众多迭代求解器有效地使用。通过使用户能够指定自己的数据类型和评估,他们可以加快一些重要的例程,并且仍然可以使用整个库的功能。

我要举的另一个例子是FEniCS和deal.II。这两个库都使用OOP来概括大量有限元方法。在元素类型,元素顺序,正交表示等所有方面都是可以互换的。尽管这两个库都比某些专用结构化FEM代码“慢”,但它们能够以用户不知道的FEM复杂性来解决各种各样的问题。

我最后一个例子是元素。Elemental是一个新的稠密线性代数库,它已将管理MPI通信器和数据位置的难度简化为非常简单的语言构造。结果是,如果您具有FLAME串行代码,则通过更改数据类型,还可以通过Elemental获得并行代码。更有趣的是,您可以通过将分布设置为相等来处理数据分布。

应该将OOP视为管理复杂性的一种方法,而不是与手动装配竞争的范例。同样做得不好也会导致大量的开销,因此必须保持定时并更新使用它的机制。


3

OOP科学计算所使用的语言功能可以使代码语句更紧凑,从而有助于更好地理解和使用代码。例如,FFT例程需要为每个函数调用携带大量参数,从而使代码繁琐。

通过使用moduleor class语句,仅可以传递调用时所需的内容,因为其余参数与问题设置(即数组的大小和系数)有关。

根据我的经验,我SUBROUTINE使用55个参数(输入和输出)进行了调用,并将其减少为5个以使代码更好。

那很值。


3

我大力倡导用于科学计算的通用编程和元编程。实际上,我正在基于称为Feel ++(http://www.feelpp.org)的这些技术为Galerkin方法开发一个免费的C ++库,并且这种方法正在稳步发展。的确,仍然存在诸如慢速编译之类的困难,并且如果想了解幕后发生的事情,学习曲线可能会陡峭。但是,这非常有趣并且令人震惊。如果是在库级别完成的,并且将复杂性隐藏在特定于域的语言背后,那么您将获得功能极为强大的工具。我们拥有非常广泛的使用和比较方法。出于科学计算的教学目的,这对于研究和新的数值方法,对于大规模应用来说都是很棒的,好吧,我们正在努力,但是到目前为止,我们已经可以做一些不错的事情。我们有工程师,物理学家和数学家使用它:他们中的大多数人只是将语言用于变式表达,他们对此感到满意。看看我们的物理学家同事操纵的一些公式,我不想看到他们没有高级语言来描述变体公式而“手工”完成。我个人认为,现在这些“技术”或“范式”对于解决科学计算代码中的复杂性(必须将代码大小乘以一个很大的因素)是必需的。可能需要改善对C ++中元编程的支持,但是特别是从C ++ 11开始,它已经处于良好状态。


2

您可能会发现与您的问题相关的论文http://arxiv.org/abs/1104.1729。它从性能的角度讨论了表达模板(科学代码中使用的模板元编程的一种特殊应用)。


那张纸使我发疯。将您拥有的最快的普通Fortran与MKL进行比较,它也会丢失。手动调校并不是人们渴望的,而是每一分之一秒都可以做的事情,并且可以被很多人重复使用。
aterrel 2012年

@aterrel:这正是我想知道的对比。知道您将必须在开发的最后阶段进行手动优化,因此您会选择哪种语言作为开发的最后阶段?我们是否有可靠的数据来建议选择哪种语言?
死息2012年

9
@Deathbreath:我也会在其他几个线程上也重复我的回答,总的来说,很少会从代码中调出最后的速度。但是您一直在编写高级算法。因此,请选择允许您快速完成重要任务的语言。总有一种方法可以以某种方式包括低级内容,但它不应该决定您对编程语言的选择。
Wolfgang Bangerth,2012年

0

模板非常擅长在运行时删除类型/域检查。这些可以在编译时处理。从理论上讲,与仅在运行时进行类型检查的C或Fortran中相同类型的实现相比,它可以提高性能-检查是在源代码中实现的。但是,您可以使用预编译器选项在C中获得相同的结果,但是与模板不同,这些操作必须手动完成。

但是,模板也会产生大量开销。他们通常会造成代码膨胀,从而影响指令缓存的使用。此外,通用方法通常会在优化过程中束缚编译器-使用通用方法时,代码分析并不总是那么简单。自动化始终是一个问题-包括编译器优化-很多时候输出代码不是缓存友好的。

类型/域检查的好处虽然肯定更安全,但是我可以在性能方面看到的唯一真正的好处,而这些通常是难以察觉的。但是正如我说的那样,总体效果可能是负面的,具体取决于您在做什么。这就是为什么在存在重大瓶颈的情况下手动优化代码通常更好的原因。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.