在科学代码中使用多种语言

计算科学 语言
2021-12-10 17:26:18

简短版:在科学代码中使用多种语言是个好主意吗?

长版:可能只有我一个人,但这些天我经常看到用多种语言编写的科学代码。争论是一种语言用于性能,另一种语言用于易用性,漂亮的语法,因为其他人都在使用它,而其他人没有使用它。

但从用户的角度来看,它是两倍的工作。我的意思是生活本来就很艰难,现在如果你想调整或扩展代码以满足你自己的需要,你必须学习的不是一种语言而是两种语言(并不是每个人都是可以快速提高生产力的明星程序员)。每个阶段都有更多的复杂性,例如,添加依赖项只是为了配置/构建代码。

在我的领域中,我看到用单一语言编写的代码往往会有很多用户随着时间的推移做出贡献。我猜其他领域的成功代码也是如此,但我不确定。

所以我的问题是,在传统科学计算中使用多种语言是一个好主意,还是过度工程的一个例子。

我能想象的唯一可能需要它的情况是当你必须耦合两个或更多代码时,即当没有简单的方法在它们之间传递/共享信息时。但通常在这些情况下,最好只重写。气候/天气建模社区做了很多耦合工作,但他们的大部分代码甚至耦合器都是用单一语言编写的。

如果您投反对票,请发表评论。

4个回答

我认为“双语方法”是合理的,我用起来感觉很舒服。

当您从头开始一个新项目时,您事先永远不会知道哪些将是您实现的关键代码部分。能够拥有可验证和基准测试的健壮且易于维护的原型代码非常重要。一旦你的原型代码工作,你就可以开始优化它。

原型制作步骤通常用表达性强且易于编码的语言(例如 python/numpy)更好地完成,而第二个步骤需要用低级语言重写代码的一小部分。一个常见的经验是,一旦优化了瓶颈,实际上就不需要重写整个代码,因为加速是微不足道的。

Knuth 的一段被大量引用的文章说

程序员浪费大量时间来思考或担心程序中非关键部分的速度,而在考虑调试和维护时,这些提高效率的尝试实际上会产生强烈的负面影响。我们应该忘记小的效率,比如大约 97% 的时间:过早优化是万恶之源。然而,我们不应该放弃那关键的 3% 的机会。

所以我的观点是,用不同的语言写这 3% 并没有错。

作为最后的评论,让我补充一点,始终希望保留慢速“高级”代码作为快速“低级”代码应该做什么的参考。

大多数科学计算可能是使用更灵活的高级语言完成的,然后使用低级语言来处理速度瓶颈。例如 python/cython/f2py、R/Rcpp、matlab/mex。著名的(或者更愤世嫉俗的,炒作的)语言 Julia 被明确设计为避免这种他们称之为“双语言问题”的多语言工作流程,例如这里解释的。

是的,如果您对后果感到满意。

工程总是涉及权衡。使用多种语言的通常理由是低级语言(Fortran、C、C++)用于性能关键的代码部分,而高级语言用于提供方便的接口和快速的代码开发。Julia、MATLAB 和 NumPy/SciPy 是此类项目的好例子。

这种分解也可以方便使用此类项目的用户;高级功能更可能用编译语言编写,而基本功能更可能用解释语言编写。此外,没有什么可以阻止项目以解释语言提供性能关键代码的“慢速”版本。

不过,您是对的,权衡可以是可维护性。如果您想自己修改或维护代码,您现在必须对两种语言而不是一种语言有足够的了解。同样,如果您选择一种语言,您就会被该语言的库、性能特征和社区所困扰。在库选择、性能等方面,使用两种语言可以让您更加灵活。

随着项目中使用的语言数量的增加,可维护性变得越来越成问题,严格的模块化分解是有帮助的。如果核心代码是用一种或两种语言编写的,并且有与其他七种语言的接口,这将很有用。(例如,PETSc 是用 C 编写的,但 C 与 C++ 非常接近,可以从 C++ 调用它,并且它具有 Fortran、MATLAB、Python 和 Mathematica 接口;我认为 Julia 接口甚至正在开发中)。如果核心代码是用多种不同的语言编写的,那么它就没有多大帮助,因为它会加剧您已经确定的可维护性问题。

我认为这取决于你在做什么。我通常会混合语言。我将在下面描述我的个人经历,也许这会帮助您决定您的应用程序。虽然我将在下面讨论我自己的工作方式,并且我选择的工具是 Mathematica,但我相信同样的优势也适用于其他高级语言。


我通常用低级 (C++) 语言编写某种类型的模拟。它有一些(数字)输入并产生一些可能复杂的数字输出。本来我是通过命令行参数和输入文件来传递输入的,然后把输出写成文本文件。

最终有必要为许多不同的输入参数运行程序,这是最好的自动化。我曾经使用一些混合或 shell 脚本和脚本语言 (Python) 使用各种参数/输入文件调用我的程序,并在 Mathematica 中处理/可视化输出。

最终我发现编写一个 Mathematica 接口来调用我的 C++ 代码比编​​写命令行接口和使用输入文件要好得多。现在这是我的标准工作流程。事实证明,Mathematica 界面更简单,更容易编写(一旦我学会了!),主要是因为无需先将其转换为文本就可以轻松传输结构化数值数据。考虑例如必须传递一个 3D 数组列表。人们需要发明一种特殊的存储格式来传输这些,或者使用 C++ 库来存储一些常见的数据格式,例如 HDF5,无论如何 Mathematica 已经有一个高级接口。

使用 Mathematica 界面还使许多事情变得非常容易,例如:

  • 参数空间的更智能映射(例如应用各种自适应 采样算法,这些算法更容易在高级语言中实现和测试);

  • 轻松保存模拟状态并稍后恢复(因为在 Mathematica 和 C 之间传输结构化数值数据很容易,我可以花时间来实现它)

  • 使用我的模拟运行各种数值算法,例如多元优化、求根等(因为 Mathematica 已经内置了许多这些算法)

  • 立即以交互方式可视化结果,无需经过几个步骤(将数据写入文件,处理以进行可视化,将其读入绘图程序)。假设一个简单的求根方法会失败,原因尚不清楚。如果我可以交互式地玩模拟,并立即将结果可视化,那么弄清楚发生了什么会容易得多。

  • 使用 Mathematica 的并行工具轻松并行运行模拟,甚至在多台计算机上(感谢 Mma 的内置通信协议)。

  • 如果模拟的输出非常大(数百 MB),我无法将所有参数值全部存储起来。我通常会对其进行一些分析(平均值、方差等),然后只存储结果。使用高级语言来驱动模拟允许我运行更多非平凡类型的分析,而无需自己在 C++ 中实现它们。

这里的重点是使用具有许多有用的内置功能的高级语言来轻松实现事物。所有这些也可以以不同的方式完成,但工作量会大得多,所以我可能不会这样做。

现在我通常在编写命令行界面之前编写 Mathematica 界面,并且仅在我确实需要它时才执行后者(例如,我必须与不使用 Mathematica 的人共享代码)。

这对我来说效果很好,因为我已经很了解 Mathematica,而且我已经用它来处理和可视化输出。我也熟悉 Mathematica 方便的 C 接口,这确实需要一些时间来学习。另外,我通常单独编写这些模拟,这并不限制我可以选择的工具。如果我在代码上与某人合作,我会选择使用我们都熟悉的工具。

当使用具有方便 C 接口的任何其他类似高级语言时,同样的优势也适用,例如Python/MATLAB/Julia/R/etc我在这里提到 Mathematica 是因为这是我所熟悉的,并且我正在描述我自己的工作流程。


我还经常使用Mathematica 中的MATLABR函数(偶尔使用 Java)。我这样做是因为这些语言之间已经存在方便的高级接口。不然我就不打扰了。我相信Python是以这种方式使用的最方便的语言之一,它将各种包的功能粘合在一起。