不将库用于标准数值算法是常见的,为什么?


54

科学计算库(如GSL)中提供了许多数值算法(积分,微分,内插,特殊函数等)。但是我经常看到带有这些功能的“手动”实现的代码。对于不一定要公开发行的小型程序,计算科学家是否普遍习惯在需要时自行实施数值算法(我的意思是从网站,数值食谱或类似软件进行复制或转录)?如果是这样,是否有特定的原因要避免链接到GSL之类的东西,还是比其他东西更“传统”?

我问是因为我是代码重用的忠实拥护者,这表明我应该尽可能尝试使用现有的实现。但是我很好奇,是否有理由认为原理在科学计算中没有在一般编程中那么有价值。


忘了提一下:我特别是在问C和C ++,而不是像Python这样的语言,使用Python有明显的好处(执行速度)。


14
一方面,您不想重新发明轮子。另一方面,理解算法的最佳方法(并通过扩展,容易地诊断出算法严重失败的情况)是尝试自己编写实现。
JM

2
您是否谴责遇到的每个定理?也许您可以试一试,并尝试一些婴儿用品,但是除非它不是您研究的重点,否则您可能会接受并继续生活。
dls

3
物理学家不是程序员,他们也不习惯处理别人的代码(读取或修改代码)。当他们不得不使用他人的代码时,通常不是由其他物理学家编写或编写得很好的代码,这再次加重了这样的想法,那就是重新编写比重新使用它更好。至少在某些领域/社区中确实如此,但年轻人的态度正在发生变化。不过,这还不尽然,考虑一下糟糕的CS学生的态度,如果他找不到足够简单的库来做某事,他将无能为力。
Szabolcs

Answers:


45

我曾经自己实现所有东西,但最近开始使用更多库。我认为使用库有几个非常重要的优点,而不仅仅是是否必须自己编写例程的问题。如果您使用图书馆,您将获得

  • 已被成千上万的用户测试过的代码
  • 无需任何工作,将来将继续更新改进的代码
  • 经过优化的代码比您初次尝试的代码更高效,更可扩展
  • 根据库的不同,通过在代码中建立到它的接口,您可以访问许多当前不使用但将来可能要使用的算法

在上面的最后一点,我想到了TrilinosPETSc之类的大型库。在开发PyClaw时,我可以举几个具体的个人例子来强化这一点。尽管将Clawpack与MPI调用并行会很简单,但我们选择使用PETSc。这使我们可以将软件包中的并行代码限制为少于300行Python,但是更好的是,通过将数据以PETSc的格式放置,我们可以立即访问PETSc的隐式求解器,从而可以在PyClaw中使用隐式求解器进行当前工作。作为第二个示例,PyClaw最初包括手码五阶WENO重构,但我们最终决定依靠PyWENO包装为此。这是一个巨大的收获,因为PyWENO可以自动生成几种语言的任意顺序的WENO例程。

最后,如果您使用库,则可以通过改进或发现错误来做出贡献,这将使许多其他人受益,而调试或改善自己的代码只会使您受益。


5
“您可以通过开发改进或发现错误来做出贡献,这将使其他许多人受益。” -既可以满足“修修补补/学习”的冲动,也可以满足懒惰(不必做已经做过的事情)。:)
JM

1
另请参见边缘情况。对于许多算法来说,实现“有效”的东西很简单,但是不能正确处理一些微小的子部分。对于一次性完成的小型项目,这可能是可以的,但是我无法数出自己对自己“优化”的某些东西被病理状况困扰的次数。
meawoppl 2015年

34

链接到库函数涉及大量程序员开销,特别是如果该库对于程序员来说是新的。仅重写简单的算法而不是弄清特定库的细节通常更简单。随着算法变得越来越复杂,这种行为也随之改变。

Python擅长通过pip / easy_install之类的工具和统一的数据结构接口来减少这种开销(即,每个库似乎都采用并生成一个numpy数组)。


19

我现在参与的一个项目是为一类粒子物理探测器编写一个灵活的模拟和分析程序包。该项目的目标之一是提供未来数十年中将在这些事物中使用代码库。

在这一点上,我们已经有两个依赖项,这使构建过程陷入了噩梦,以至于它分离出了一个由Fermilab计算中心管理的单独项目,只是为了提供可靠的工具链。

现在想象一下,您遇到了对该工具链中没有的某些工具的需求(上个月刚发生在我身上)。你有三个选择

  1. 滚你自己的。涉及所有的风险和麻烦。
  2. 从某个地方的库中删除一些代码,并将其包含在Project中。这意味着您必须继续进行维护,并且在发生这种情况时必须了解其他人的代码。
  3. 向维护工具链的人员求助,请他们为您提供所需的信息,然后等待发布周期来获取它。这些人反应灵敏,但是您必须在没有工作代码的情况下在完成(1)或(2)之后进行辩护。

选择(1)非常容易。也许太容易了。


是的,添加的依赖项是使用库的一个重大缺点。
David Ketcheson 2011年

依赖也是我心中最大的缺点
Fomite 2011年

2
我的答案很可能是过于依赖依赖项,而对于在大型项目中安装批准依赖项的广告的官僚主义过程却不够。
dmckee 2011年

*您输入的是第3点。(对不起,我很高兴。)
299792458

呃没有。它说了我的意思。
dmckee

12

我认为这相当普遍,有一些算法更可能比别人重新实现。

在库的安装方式,如何自己实现算法的难度,如何优化算法的难度以及库如何满足您的需求之间需要一个棘手的权衡。另外,有时使用库仅仅是个过​​大的事情:我在一个程序中使用了慢速二等分算法,因为我只调用了几次,而我不想为此添加一个库。

您编写一个经过优化的版本是否容易?如果是这样,您最好这样做。您将完全得到所需的东西,而不必依赖任何人的工作。但是,当然,您确实需要知道自己在做什么:即使是简单的算法也很难实现。

我很想看到对此进行研究,但是从我的偏见来看,科学家经常将库用于线性代数和随机数生成器,而其余大部分代码都是自制的。


12
“但是当然,您确实需要知道自己在做什么:即使是简单的算法也很难实现。” -这不能足够强调。
JM

10

我认为实现算法而不是使用库有时可以更好地理解和控制模型。当我编写一些用于科学计算的程序时,了解自己在做什么很重要。实施重要的算法有助于我更好地了解问题并更好地控制问题。

另一方面,有时选择要获得解决方案所需的库并不是一件容易的事,因此,在确定要实现的目标以及为什么要实现时,最好搜索已经实现的算法。

如果算法很复杂,那么手工编码就可以使您有机会使用特定于任务的功能来提高解决方案的性能/质量。有时有必要稍微改变一下算法,如果您知道所编写的代码并且可以按自己的方式进行编辑,则这会更容易。


1
+1可增进理解。尽管对于您自己的算法而言,对于库例程而言,这更是一个问题。
Faheem Mitha

8

一个答案是数字代码的细微变化太多,以至于很难将其封装在库中。与Web软件相比,此软件通常易于安装并且具有清晰的输入和输出集。我认为,更常见的是人们抓住一个框架或行为类似于框架的大型库(Trilinos / PETSc),并使用该生态系统来获得使用社区代码的好处。


7

我认为在决定是否使用库之前,我还想弄清楚使用库对您的代码有多大帮助。如果您打算对密钥计算内核使用经过优化的库,那么它可能比尝试编写自己的库更有效。

但是,如果您编写的专用例程在程序执行期间只会被调用一次,那么修改代码以适合库所需的框架可能就不值得了。

(另一件需要考虑的事情:要利用该库,您需要做多少重新构架?除非您为修复代码花费的工时得到了效率或数值准确性的相应补偿,否则可能不会从长期来看是值得的。但是,理想情况下,这是您在最初设计数据结构和算法时要计划的事情,以便从头开始考虑库的“流程”。)


6

我的2美分。

我认为对此进行一般性的编写要容易得多,而不仅仅是C / C ++。首先,不一定要使用Python之类的语言库来获得速度上的好处,即使这样做是后果。我认为 @David很好地说明了原因。

从顶部开始,语言的实现在某种程度上决定了您可以访问的库。计算科学中常用的语言包括C,C ++,Python,Perl,Java,Fortran和R。较少见的示例可能是Ocaml和Common Lisp。现在,由于这些语言大多数都是用C编写的,因此它们具有C的自然外部函数接口。但是,要调用Python的Perl库并不容易,反之亦然。所以在实践中人们倾向于

  1. 使用以其实现语言编写的库,通常是标准库的一部分,或者以其他方式广泛使用,或者

  2. 通过语言FFI调用C / C ++库。这假定包装器尚不存在,因为如果存在,则很难与(1)区分开。

(2)通常比较困难,因为您必须自己包装C / C ++函数。另外,您必须捆绑库,或添加额外的依赖项。因此,人们更倾向于使用内置语言库,而不是使用C语言中的GSL。

对于非常通用的例程,比如说从分布中生成随机样本,或者是诸如积分求积之类的基本数值例程,重用某些库很容易且普遍。随着人们试图实现的功能变得越来越复杂,人们越来越不可能在另一个库中找到一个人想要的确切函数,甚至一个人也可能花大量时间搜索并最终适应该函数,这成倍地不可能。必要的(例如,代码样式/设计可能是个问题)。并且如上所述,人们只能访问那里的一部分库。另一方面,如果算法本身很复杂而不是主要关注点,那么实现自己的算法可能会令人望而生畏,当然,人们必须解决那些讨厌的速度问题。

因此,这成为成本/收益分析中的优化问题。我的经验是,即使对于像MCMC这样的相对标准的技术,我通常也会写自己的代码,因为它更适合我设计整体软件的方式。

当然,即使您最终不使用该代码,也可以向其他人的代码学习。不过,我不知道科学家实际上有多频繁地这样做。我的印象是,阅读他人的代码以学习的更多是软件工程师的工作。


6

回想我第二年的机械课程,我想到我实现自己的著名算法版本的部分原因是,我被教导要这样做。我想不出一个例子,在我的本科物理教育中,我被教过如何与图书馆连接和链接。我确实有幸第一次看到旋转的高尔夫球的坐标列表,自己计算了FORTRAN中耦合的牛顿方程的解。从头开始计算事物会带来一定的刺激和满足感(甚至是自豪感)。


1
这当然是一个因素。而且,对于计算机科学家的一部分教育来说,自己专注于执行此任务是必要的。纯粹的程序员有时会把它淘汰掉,但是我们的科学类型可能会从该入门教室转移到一个几乎完全由其他人组成的项目中。
dmckee 2012年

5

我认为应该尽可能使用经过测试的库。大多数人不是数值计算方面的专家,可能无法像经过良好测试的库中提供的那样正确,仔细地实施解决方案。就是说,然而,有时候是没有可用的库来实现给定应用程序所需的功能组合的情况。我已经看到这种情况发生在我工作的技术领域。现有的代码无法解决所有情况,最终有人从头开始实现了一个求解器。


1
如果库不能满足您的所有需求,建议您扩展库代码并提交补丁。这样一来,您的工作将使许多其他人受益,其他人也将为您测试代码。当然,这假定库代码​​是以足够灵活的方式编写的,可以对其进行扩展以满足您的需求。
大卫·凯奇森2011年

我同意,这是一个很好的解决方案,人们应该尽可能做些事情。
mhucka 2011年

5

基本问题通常是应用程序与库之间的接口。应用程序程序员具有将问题(例如,作为矩阵)传递给库时经常会丢失的问题的知识。这种知识使得利用它不仅仅抵消了使用高度优化的库的好处。结果,应用程序程序员“滚动”了他/她的利用知识的实现。

因此,一个真正好的库需要将此类知识从应用程序传递到库中,以便库也可以利用它。


3

除了上面已经说过的所有内容之外,我还将重复“ Fortran vs C ++”问题的答案:程序员拥有的最宝贵的资产是她的时间。是的,外部依赖关系通常很尴尬。但是,花时间重新实施,调试和测试其他人已经实现的算法几乎总是愚蠢的,结果也很少像专家针对特定主题编写的代码那样好。重用他人所做的事情!


我对这个话题有自己的答案。当您重写所有细节时,您可以学到更多。我现在在点云领域工作了5-6年。头三年,我自己编写了所有功能。后半部分我使用了点云库。我无法证明,但是我认为自己是PCL的更强专家,因为前三年花在了思考其他人已经提供的解决方案上。
Jan Hackenberg

@JanHackenberg-是的,但我也要直言不讳:您只是浪费了三年的时间来重新发明轮子。想象一下,如果您使用别人做过的事情,可以做多少工作!
Wolfgang Bangerth

我决定在博士学位的第一年就用Java编写程序,因为此时我认为自己的编程技能(不是信息学的理论)接近于零。Java仍然是我最擅长的语言。由于容易获得多平台支持,我还认为Java是不错的选择。我进入了在phd(传统林业)中没有任何信息支持的椅子。当我意识到自己的错误并且可以(发布之后,而不是之前)时,我跳到了c ++。
Jan Hackenberg

顺便说一句,我不同意我生命的三年。这意味着我在phd博士后的经验中只有两年有用的时间。今天,我可以在林业点云中安装100亿个气瓶,然后让机器确定哪些代表树木。我的〜50个用户也可以这样做。在约1小时内。通过学习困难而费时的方法,我学到了所有技巧。我决定永远不学习如何使用vi,但是当人们通过所需的学习曲线时,我声称他们会使用最有效的方式来生成代码。
Jan Hackenberg

2

我与之合作的小组尽可能使用库。我是少数几个程序员之一,其他人则从事这项工作。他们知道自己的局限性,足以知道自己不应该涉足何处。IMSL是首选的库。由于许可证限制,将禁止使用类似GSL之类的东西,即使这是联邦机构,我们仍然会放弃我们的软件。


2

“重用主要是一种社会现象。我可以使用他人的软件,前提是

  1. 有用
  2. 这是可以理解的
  3. 它可以共存
  4. 支持它(或者我愿意自己支持它,大多数情况下我不支持)
  5. 这是经济的
  6. 我可以找到它。

“ — B. Stroustrup,《 C ++编程语言》第2版(1991年),第383页。


1

其他人给出了使用库以及滚动自己的例程的几个很好的理由。

有时您可以加快特定应用程序的计算速度,因为事先知道您将永远不需要库例程涵盖的各种值或这些例程提供的准确性。

当然,很大程度上取决于特定的应用程序以及库例程将被调用多少次。如果只需要为x的较小范围使用几个有效数字,而您需要一些简单的技术就可以满足您的需求,为什么为什么要为贝塞尔函数调用数十亿次的库例程?


0

没什么可添加的,我们必须重用代码,这与代码的可持续性和对社会的贡献有关,但仅此而已。

我们不重用代码的原因是,如果您刚开始,程序员将很难理解其他代码。使用高级C ++尤其困难,您也可以在纯C语言中做一些技巧。

在开始时,人们常常会理解该方法,但并不理解该方法在库中的实现方式,或者仅仅是如何使用具有通用接口,错误控制和约定的库,如果有的话,通常会为有经验的程序员提供文档。这给出了更好地实现标准方法的错觉,例如您自己进行LU分解。而且,新程序员低估了不同操作系统的代码测试,验证和可移植性的价值。因此,最终的原因是懒惰,编写自己的代码看起来像是一种更快,更轻松的解决方案。

现实情况是,与从头开始编程相比,我们可以通过使用和阅读代码来学习更多。

懒惰在大多数时间驱使我,我想大多数人也是如此。出于同样的原因,有些人从头开始编写代码,而另一些人则使用现有的库。


-1

库算法与自己的实现相反:

  • 它们是通用的和模板化的。您以后可以重新参数化您的实现,而不必担心更改自己的代码(该代码应该有很多约束)。
  • 输入数据既省钱又退化。许多计算几何算法(例如凸包算法)需要处理例如三点的共线性。如果您从不打算分发代码,也不想以后再使用代码,则可以忽略这些情况。
  • 它们为预期或最坏情况的输入配置提供了最小的运行时复杂性。较高级别的算法通常具有较低级别的算法(例如,排序算法或特殊数据类型)作为构建模块。快速排序可能是对数据进行排序的最常见选择,但是如果算法的实现必须保证n(log(n)),则不能使用它。
  • 它们提高了内存使用效率
  • 他们进一步优化了运行时
  • 如果得到支持,则通常更加封闭,以免出现“ bug”,尤其是在使用main分支的情况下。没有什么比分布式数据库更好地测试了。并非每个bug都会崩溃,也不是每个bug都会产生不合理的结果。算法的实现可能仍会产生可接受的结果,只是效果不如预期。错误越不明显,作为一个人的您甚至无法检测到该错误。

我仍然认为在进入一个新领域自己实现一个易于理解的算法的版本时会很好。我总共要花很多时间。我购买并阅读了名为Press等的书籍。在实施之前和期间,我总是读很多理论。在我理解了领域的一般概念并经历了实践中的陷阱之后,该是时候从各个方面着手更好的库实现了。我认为,如果您自己在Librarys字段中编写“ hello world”算法,您将成为该库的更好用户。

如果您在更大的团队中工作,那么您的团队是否使用特定的库可能不是您自己的选择。核心团队可能会做出决定。并且可能会有人负责您项目中的库绑定及其自己的时间计划。重写您可以根据自己的时间计划进行的一种算法,而不必依赖其他人的决定。

如果您自己一个人并且喜欢分发,则还有另一个问题。我认为许多其他源代码也是最有用的资源。在这里,许多信息学家可能会同意。在信息学之外的应用领域中,可能需要在Windows上提供预编译的可执行文件。在Linux下,如果使用开源使用库,则可以相对轻松地自行设置。

自行重写算法可以自由使用。例如,您的项目可能不支持GSL的GPL许可。

从研究者的角度来看,这种沉默可能是唯一的约束。


1
认为“自己实现算法”和“学习库语法”会“花费相同的时间”是荒谬的。对于诸如“ strcat”之类的简单功能,甚至都不是这样。例如,对于LAPACK中或更高级别的库中的任何东西,绝对不是这种情况。
Wolfgang Bangerth '17

@WolfgangBangerth感谢您的反馈。我重读了我写的内容,但我不想传递这样的信息,即自己的实现可能具有竞争优势。但是我学到了很多。我的“可能要花两个星期”并不是一个很好的例子。实际上,当我从Java切换到C ++时,这确实花了我2个星期的最后一次“学习语法”,这时我还学习了基本的C ++语法。我与Pointers的斗争更多,而与新图书馆的斗争则更多。在我执行的任何算法上,有两个星期可能是编码时间,这是我的小笔投资(之前花很多时间看书)。
Jan Hackenberg

投资本身并不是在编写一个小的算法。那是很快的,实际上有时有时只要学习另一个图书馆就可能需要很长时间。花费大量时间进行调试和正确处理所有极端情况。如果您使用完善的库,则知道矩阵向量乘积适用于正方形矩阵,它也适用于矩形矩阵。对于您自己的软件,即使您认为已经完成了该功能,也可以实现和调试它。您将多次返回同一功能。那就是花费时间。
Wolfgang Bangerth '17

@WolfgangBangerth我同意您的所有论点。我唯一要说的是,当您需要自己处理那些角落案例时,您会学到更多的理论。我的答案的第一个版本确实听起来没什么区别。我太累了。我在改进的答案中写了很多关于库稳定性的好处。对我来说,这是花费的时间和所获得的知识之间的权衡。
Jan Hackenberg
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.