Haskell是否适合长时间运行的应用程序?

Is Haskell appropriate for long-running applications?

提问人:MWB 提问时间:5/27/2015 最后编辑:MWB 更新时间:6/5/2015 访问量:3265

问:

我认为Haskell是一门漂亮的语言,从基准测试来看,它的实现可以生成快速的代码。

但是,我想知道它是否适合长时间运行的应用程序,或者追逐所有潜在的懒惰引起的泄漏,在短期应用程序中可能会忽略这些泄漏,是否会令人沮丧?

Reddit的这条评论呼应了我的担忧:

只要有多个函数递归调用自身, 堆配置文件不再为您提供任何帮助,以查明 正在发生泄漏。

(整个讨论似乎很有见地和坦率)

我个人对高性能计算很感兴趣,但我想服务器和 HPC 有这个共同点。

如果Haskell适合这样的应用,有没有例子可以证明这一点,也就是说,应用

  1. 需要运行数天或数周,因此需要消除所有相关的泄漏(程序花费在睡眠或等待某些底层 C 库返回上的时间显然不算在内)
  2. 是不平凡的(如果应用程序很简单,开发人员可以猜测泄漏的来源并尝试各种修复。但是,我不认为这种方法可以很好地扩展。根据上面的 Reddit 讨论,堆配置文件在识别具有多个 [相互] 递归函数的泄漏源方面的帮助似乎特别值得关注)

如果Haskell不适合这样的应用,那为什么呢?

更新:Haskell 的 Yesod Web 服务器框架(作为示例提出)可能存在内存问题。我想知道是否有人在连续几天为请求提供服务后测试了它的内存使用情况。

性能 Haskell 内存泄漏 函数式编程 科学计算

评论

2赞 willeM_ Van Onsem 5/27/2015
看起来有点像带有垃圾收集器的系统是否合适:因为人们通常不会销毁不再需要的对象:他们认为 gc 最终会找到它们。但这可能会导致大量堆对象仅处于活动状态,因为引用未设置为使所有这些对象成为垃圾。gcnull
6赞 bheklilr 5/27/2015
懒惰并不意味着空间泄漏,就像严格并不意味着。管理这两种内存模型有不同的技术。编写应用程序的方式决定了应用程序是否能够长时间运行。我知道 Facebook 正在使用 Haskell 作为多个数据存储和一些前端服务之间的中间层,但我不知道这些是否是短暂的过程。我的猜测是,它们需要长时间运行,所以如果是这样的话,你会有一个非常可靠的例子。
0赞 willeM_ Van Onsem 5/27/2015
@bheklilr:我不认为 MaxB 指的是空间泄漏:Haskell 正确地管理内存(或者应该从理论角度来看),但回收死物可能需要很长时间。
3赞 luqui 5/27/2015
@MaxB,你不能在GC语言中真正“删除所有垃圾”。我们谈论的是忘记将某些引用设置为 ,这与不计算某些表达式所引用的内容非常相似。然而,与命令式程序相比,在Haskell程序中推理内存确实非常困难。你可以设计你的持久数据结构,以保证它们不会有未经评估的砰砰声——如果我写的是一个巨大的系统,我可能会这样做。它确实限制了您的表现力,但也为内存使用提供了一个检查点。null
1赞 Jedai 5/27/2015
阅读此页: engineering.imvu.com/2014/03/24/what-its-like-to-use-haskell .Haskell似乎在长期运行的服务中运行良好,但是空间泄漏可能更难发现(尽管工具正在改进,所以我不知道现在有多难)。

答:

8赞 Jeremy List 5/27/2015 #1

warp Web 服务器证明 Haskell 适用于长时间运行的应用程序。

当 Haskell 应用程序出现空间泄漏时,可能很难追踪原因,但一旦知道原因,解决起来通常很简单(我曾经使用过的最困难的修复方法是应用于列表并从最后一个元素中获取长度,而不是使用函数)。但是在Haskell程序中,空间泄漏实际上非常罕见。通常,故意制造空间泄漏比修复意外泄漏更难。zip [1..]length

评论

0赞 MWB 5/27/2015
The warp web server proves...是否有任何繁忙的网站使用它?
3赞 Jeremy List 5/27/2015
github.com/yesodweb/yesod/wiki/Powered-by-Yesod 有一个不完整的网站列表,这些网站使用 Yesod 框架(这很难与另一个 Web 服务器一起使用)。似乎没有人那么忙,但有时很难说。另一方面:在基准测试中,warp 每秒处理的请求比 nginx 多,但在单核服务器上除外。在 10 核服务器上:warp 比 nginx 快 5 倍。
6赞 usr 5/28/2015 #2

大多数长时间运行的应用都是请求驱动的。例如,HTTP 服务器将所有瞬态数据与 HTTP 请求相关联。请求结束后,数据将被丢弃。因此,至少对于这些长时间运行的应用程序,任何语言都不会有空间泄漏。在单个请求的上下文中泄漏您想要的所有内容。只要您不创建对每个请求数据的全局引用,您就不会泄漏。

如果你改变全局状态,所有的赌注都是失败的。由于多种原因,这是要避免的,这在此类应用程序中并不常见。

评论

0赞 MWB 5/28/2015
我不是 Web 程序员,但我认为大多数 HTTP 服务器在处理请求后需要保留一些信息:日志记录、新内容(如在本站点上)、库存物品等。
0赞 luqui 5/29/2015
@Carsten,所以也许我们正在实现一个数据库或类似的东西。
0赞 Random Dev 5/29/2015
@luqui ???好吧,看来我是那种奇怪的孩子,你不想在这里玩——所以好吧,我已经闭嘴了
0赞 luqui 5/29/2015
我想你听到了我为了自我满足而定义问题的挫败感,这似乎发生在人们提出我们喜欢的语言的问题时......
4赞 Yuras 6/2/2015 #3

我有一个用 haskell 编写的服务,可以运行数月,没有任何特定于 haskell 的问题。有一段时间它工作了 6 个月而没有任何关注,但后来我重新启动了它以应用更新。它包含一个无状态的 HTTP API,但它也有状态完整的 websockets 接口,因此它保持长期存在状态。它的源代码是封闭的,所以我无法提供链接,但我的经验 haskell 适用于长时间运行的应用程序。

懒惰对我来说不是问题,但那是因为我知道如何处理它。这并不难,但需要一些经验。

此外,hackage 上的库具有不同的质量,控制依赖关系是一件重要的事情。我尽量避免依赖关系,除非它们真的有必要,我会检查它们的大部分代码(除了一些广泛使用的包,它们中的大多数要么是核心库,要么是Haskell平台的一部分,尽管我也检查它们的代码——只是为了学习新东西。

尽管在一些极端情况下,GHC(使用最广泛的实现)工作得不够好。当应用程序在内存中保持巨大(主要是只读)状态(有票证)时,我遇到了 GC 时间问题。此外,许多稳定的指针也可能有问题(票证,尽管我自己从未体验过。大多数情况下,通过精心设计,这种极端情况很容易避免。

实际上,对于长时间运行的应用程序来说,应用程序设计是最重要的。实现语言的作用不那么重要。这可能是我过去几年学到的最大教训——软件设计非常重要,语言之间没有太大区别。

17赞 Don Stewart 6/2/2015 #4

“空间泄漏”在语义上与任何语言中的任何其他类型的资源使用问题相同。在严格的语言中,GC 往往会分配和保留过多的数据(因为结构很严格)。

无论使用哪种语言,您都应该进行一些“老化”,以查找随时间推移的资源使用情况,Haskell 也不例外。

例如,请参见 ,它一次运行数月或数年。这是一个 Haskell 应用程序,堆使用量很小,我通过运行数周或数月来分析堆模式来测试它。这让我确信资源使用是稳定的。xmonad

但归根结底,懒惰在这里是一条红鲱鱼。使用资源监视工具和测试来衡量和验证您的资源预期。

评论

2赞 MWB 6/2/2015
xmonad具有非常低的复杂度(< 1KLOC)。目前尚不清楚通过查看分析器来追踪泄漏将如何扩展,并且 99.9% 的时间不会睡觉?(你怎么说?在这种类型的应用程序中使用 Haskell 真的是最好的例子吗?xmonadtopxmonad
0赞 Don Stewart 6/5/2015
xmonad 的核心是 < 1K,与 Web 服务器的核心非常相似。如果您对“长时间运行”有不同的要求,请说明您的意思。
1赞 MWB 6/5/2015
我只是澄清了问题中的要求。
8赞 Petr 6/3/2015 #5

是的。有两种可能的空间泄漏:

堆上的数据。这里的情况与其他使用 GC 的语言没有什么不同。(对于那些没有这样做的人,情况通常会更糟 - 如果出现错误,该过程可能会触及释放的内存,反之亦然,并且会严重崩溃,而不是增加内存使用量。

未经评估的砰砰声。诚然,一个人可以搬起石头砸自己的脚, 当然,必须避免产生大重击的众所周知的情况,例如.但是防止这种情况并不难,对于其他泄漏,我会说,当你习惯它时,它实际上比其他语言更容易。foldl (+) 0

您要么具有长时间运行的繁重计算,要么具有响应请求的服务。如果计算时间较长,则通常需要在计算结果时立即获得结果,这会强制对其进行评估。

如果你有一个服务,它的状态通常是很好的,所以很容易确保它总是在请求结束时被评估。事实上,与其他语言相比,Haskell 使这变得更容易:在 Haskell 中,你不能让程序的组件保持自己的内部状态。应用程序的全局状态要么在某种主循环中作为参数线程化,要么使用 .而且,由于 Haskell 应用程序的良好设计会尽可能地限制和本地化,因此它再次使状态易于控制。IOIO


再举一个例子,Ganeti 项目(我是开发人员)使用了几个 Haskell 长时间运行的守护进程。

根据我们的经验,内存泄漏非常罕见,如果我们遇到问题,通常是其他资源(如文件描述符)的问题。我记得最近唯一一个案例是监控守护程序在极少数情况下将内存泄漏为砰砰声,它收集数据,但没有人查看它们(这将迫使它们进行评估)。修复相当简单

0赞 Cigarette Smoking Man 11/30/2023 #6

真正的答案是,是的,Haskell适合长时间运行的进程。与其他语言一样,由开发人员来查找和修复任何错误,包括内存泄漏。

要做到这一点,如果你的程序不依赖于懒惰,那么从 GHC 8.0.1 (2016) 开始支持 StrictDataStrict 语言扩展,它们可以梳理出空间泄漏。(懒惰与严格是一个有争议的话题,所以在你把它放进阴谋集团档案之前,先咨询别人:))

cardano-node 是一个长时间运行的大内存占用程序的例子。

作为工作的一部分,我有两个 Web 应用程序,它们一次运行数月,并且仅由于不相关的原因而重新启动,例如更新 Let's Encrypt 或更新操作系统。(两者都建立在 Warp 之上,所以功劳可能属于那里)