提问人:MWB 提问时间:5/27/2015 最后编辑:MWB 更新时间:6/5/2015 访问量:3265
Haskell是否适合长时间运行的应用程序?
Is Haskell appropriate for long-running applications?
问:
我认为Haskell是一门漂亮的语言,从基准测试来看,它的实现可以生成快速的代码。
但是,我想知道它是否适合长时间运行的应用程序,或者追逐所有潜在的懒惰引起的泄漏,在短期应用程序中可能会忽略这些泄漏,是否会令人沮丧?
Reddit的这条评论呼应了我的担忧:
只要有多个函数递归调用自身, 堆配置文件不再为您提供任何帮助,以查明 正在发生泄漏。
(整个讨论似乎很有见地和坦率)
我个人对高性能计算很感兴趣,但我想服务器和 HPC 有这个共同点。
如果Haskell适合这样的应用,有没有例子可以证明这一点,也就是说,应用
- 需要运行数天或数周,因此需要消除所有相关的泄漏(程序花费在睡眠或等待某些底层 C 库返回上的时间显然不算在内)
- 是不平凡的(如果应用程序很简单,开发人员可以猜测泄漏的来源并尝试各种修复。但是,我不认为这种方法可以很好地扩展。根据上面的 Reddit 讨论,堆配置文件在识别具有多个 [相互] 递归函数的泄漏源方面的帮助似乎特别值得关注)
如果Haskell不适合这样的应用,那为什么呢?
更新:Haskell 的 Yesod Web 服务器框架(作为示例提出)可能存在内存问题。我想知道是否有人在连续几天为请求提供服务后测试了它的内存使用情况。
答:
warp Web 服务器证明 Haskell 适用于长时间运行的应用程序。
当 Haskell 应用程序出现空间泄漏时,可能很难追踪原因,但一旦知道原因,解决起来通常很简单(我曾经使用过的最困难的修复方法是应用于列表并从最后一个元素中获取长度,而不是使用函数)。但是在Haskell程序中,空间泄漏实际上非常罕见。通常,故意制造空间泄漏比修复意外泄漏更难。zip [1..]
length
评论
The warp web server proves...
是否有任何繁忙的网站使用它?
大多数长时间运行的应用都是请求驱动的。例如,HTTP 服务器将所有瞬态数据与 HTTP 请求相关联。请求结束后,数据将被丢弃。因此,至少对于这些长时间运行的应用程序,任何语言都不会有空间泄漏。在单个请求的上下文中泄漏您想要的所有内容。只要您不创建对每个请求数据的全局引用,您就不会泄漏。
如果你改变全局状态,所有的赌注都是失败的。由于多种原因,这是要避免的,这在此类应用程序中并不常见。
评论
我有一个用 haskell 编写的服务,可以运行数月,没有任何特定于 haskell 的问题。有一段时间它工作了 6 个月而没有任何关注,但后来我重新启动了它以应用更新。它包含一个无状态的 HTTP API,但它也有状态完整的 websockets 接口,因此它保持长期存在状态。它的源代码是封闭的,所以我无法提供链接,但我的经验 haskell 适用于长时间运行的应用程序。
懒惰对我来说不是问题,但那是因为我知道如何处理它。这并不难,但需要一些经验。
此外,hackage 上的库具有不同的质量,控制依赖关系是一件重要的事情。我尽量避免依赖关系,除非它们真的有必要,我会检查它们的大部分代码(除了一些广泛使用的包,它们中的大多数要么是核心库,要么是Haskell平台的一部分,尽管我也检查它们的代码——只是为了学习新东西。
尽管在一些极端情况下,GHC(使用最广泛的实现)工作得不够好。当应用程序在内存中保持巨大(主要是只读)状态(有票证)时,我遇到了 GC 时间问题。此外,许多稳定的指针也可能有问题(票证,尽管我自己从未体验过。大多数情况下,通过精心设计,这种极端情况很容易避免。
实际上,对于长时间运行的应用程序来说,应用程序设计是最重要的。实现语言的作用不那么重要。这可能是我过去几年学到的最大教训——软件设计非常重要,语言之间没有太大区别。
“空间泄漏”在语义上与任何语言中的任何其他类型的资源使用问题相同。在严格的语言中,GC 往往会分配和保留过多的数据(因为结构很严格)。
无论使用哪种语言,您都应该进行一些“老化”,以查找随时间推移的资源使用情况,Haskell 也不例外。
例如,请参见 ,它一次运行数月或数年。这是一个 Haskell 应用程序,堆使用量很小,我通过运行数周或数月来分析堆模式来测试它。这让我确信资源使用是稳定的。xmonad
但归根结底,懒惰在这里是一条红鲱鱼。使用资源监视工具和测试来衡量和验证您的资源预期。
评论
xmonad
具有非常低的复杂度(< 1KLOC)。目前尚不清楚通过查看分析器来追踪泄漏将如何扩展,并且 99.9% 的时间不会睡觉?(你怎么说?在这种类型的应用程序中使用 Haskell 真的是最好的例子吗?xmonad
top
xmonad
是的。有两种可能的空间泄漏:
堆上的数据。这里的情况与其他使用 GC 的语言没有什么不同。(对于那些没有这样做的人,情况通常会更糟 - 如果出现错误,该过程可能会触及释放的内存,反之亦然,并且会严重崩溃,而不是增加内存使用量。
未经评估的砰砰声。诚然,一个人可以搬起石头砸自己的脚,
当然,必须避免产生大重击的众所周知的情况,例如.但是防止这种情况并不难,对于其他泄漏,我会说,当你习惯它时,它实际上比其他语言更容易。foldl (+) 0
您要么具有长时间运行的繁重计算,要么具有响应请求的服务。如果计算时间较长,则通常需要在计算结果时立即获得结果,这会强制对其进行评估。
如果你有一个服务,它的状态通常是很好的,所以很容易确保它总是在请求结束时被评估。事实上,与其他语言相比,Haskell 使这变得更容易:在 Haskell 中,你不能让程序的组件保持自己的内部状态。应用程序的全局状态要么在某种主循环中作为参数线程化,要么使用 .而且,由于 Haskell 应用程序的良好设计会尽可能地限制和本地化,因此它再次使状态易于控制。IO
IO
再举一个例子,Ganeti 项目(我是开发人员)使用了几个 Haskell 长时间运行的守护进程。
根据我们的经验,内存泄漏非常罕见,如果我们遇到问题,通常是其他资源(如文件描述符)的问题。我记得最近唯一一个案例是监控守护程序在极少数情况下将内存泄漏为砰砰声,它收集数据,但没有人查看它们(这将迫使它们进行评估)。修复相当简单。
真正的答案是,是的,Haskell适合长时间运行的进程。与其他语言一样,由开发人员来查找和修复任何错误,包括内存泄漏。
要做到这一点,如果你的程序不依赖于懒惰,那么从 GHC 8.0.1 (2016) 开始支持 StrictData
和 Strict
语言扩展,它们可以梳理出空间泄漏。(懒惰与严格是一个有争议的话题,所以在你把它放进阴谋集团档案之前,先咨询别人:))
cardano-node 是一个长时间运行的大内存占用程序的例子。
作为工作的一部分,我有两个 Web 应用程序,它们一次运行数月,并且仅由于不相关的原因而重新启动,例如更新 Let's Encrypt 或更新操作系统。(两者都建立在 Warp 之上,所以功劳可能属于那里)
评论
gc
null
null