【问题标题】:How do you write data structures that are as efficient as possible in GHC? [closed]如何在 GHC 中编写尽可能高效的数据结构? [关闭]
【发布时间】:2011-09-19 21:50:56
【问题描述】:

所以有时我需要编写一个在 Hackage 上找不到的数据结构,或者我发现的数据结构没有经过测试或质量不足以让我信任,或者这只是我不想成为依赖项的东西。我现在正在阅读冈崎的书,它很好地解释了如何设计渐近快速的数据结构。

但是,我专门与 GHC 合作。常数因素对我的应用程序来说很重要。内存使用对我来说也很重要。所以我有一些关于 GHC 的问题。

特别是

  • 如何最大限度地共享节点
  • 如何减少内存占用
  • 如何避免因不适当的严格/懒惰导致的空间泄漏
  • 如何让 GHC 为重要的代码部分生成紧密的内部循环

我浏览了网络上的各个地方,我对如何使用 GHC 有一个模糊的想法,例如,查看核心输出、使用 UNPACK pragmas 以及喜欢。但我不确定我是否明白。

所以我打开了我最喜欢的数据结构库、容器,然后查看了 Data.Sequence 模块。我不能说我了解他们为加快 Seq 所做的很多工作。

首先引起我注意的是FingerTree a的定义。我想这只是我对手指树不熟悉。引起我注意的第二件事是所有SPECIALIZE 编译指示。我不知道这里发生了什么,我很好奇,因为这些代码到处都是。

许多函数还具有与之关联的INLINE pragma。我可以猜到这意味着什么,但我如何判断何时调用 INLINE 函数?

在 ~475 行,标题为“应用构造”的部分变得非常有趣。他们定义了一个新类型的包装器来表示 Identity monad,他们编写了自己的严格状态 monad 的副本,并且他们定义了一个名为 applicativeTree 的函数,它显然是专用于 Identity monad 的,这增加了功能。我不知道这里发生了什么。什么巫术被用来增加分享?

无论如何,我不确定是否可以从 Data.Sequence 中学到很多东西。我可以阅读其他“示范程序”来获得智慧吗?当我真的需要它们更快时,我真的很想知道如何增强我的数据结构。特别是一件事是编写使融合变得容易的数据结构,以及如何编写好的融合规则。

【问题讨论】:

  • 如果我没记错的话,这个问题的标准答案是“将您的问题添加到 the language benchmarks game 并等待 Don 为您优化它”。 ;]
  • 哈哈哈。也许我应该吃掉 Dons,从而获得他创造与 C 竞争的 Haskell 代码的不可思议的力量。

标签: data-structures haskell


【解决方案1】:

这是一个很大的话题!大多数已经在别处进行了解释,所以我不会尝试在这里写一本书的章节。而是:

  • Real World Haskell,第 25 章,“Performance” - 讨论分析、简单的专业化和解包、读取 Core 以及一些优化。

Johan Tibell 写了很多关于这个主题的文章:

还有一些来自这里的东西:

还有一些其他的:

【讨论】:

  • 那么specialize pragma 是否类似于您对数据结构的优化,但自动完成并针对函数?
  • 是的。尽管考虑到 INLINE 的存在,这有点不必要
  • SPECIALIZE 比 INLINE 导致更少的代码膨胀,因为每个类型只有一个额外的函数体,而不是每个调用站点一个额外的函数体。我的经验法则:对具有高阶参数的函数使用 INLINE(以避免间接调用),否则使用 SPECIALIZE。随着 INLINABLE 的添加,我在以前使用 SPECIALIZE 的地方使用了几乎独占的,因为 INLINABLE 允许在不同的模块中进行专业化。
  • 我在 CUFP 的“High-Performance Haskell”演讲包含“Reasoning about laziness”幻灯片的超集。你可以在这里找到它:blog.johantibell.com/2010/09/…
  • Edward Zhang 有一个 interesting post 对树的映射,这种方式可以在函数结果相同时获得最大共享。
【解决方案2】:

applicativeTree 非常奇特,但主要是在某种程度上与 FingerTrees 相关,它们本身就是一种奇特的数据结构。我们对over at cstheory 的复杂性进行了一些讨论。请注意,applicativeTree 是为在 any Applicative 上工作而编写的。碰巧当它专门用于Id 时,它可以以其他方式无法共享的方式共享节点。您可以通过内联 Id 方法并查看会发生什么来自己完成专业化。请注意,这种特化只用在一个地方—— O(log n) replicate 函数。更通用的函数巧妙地专门用于常量情况这一事实是代码重用的一个非常聪明的地方,但仅此而已。

我认为,总的来说,Sequence 教授的更多是关于设计持久数据结构的知识,而不是所有获取性能的技巧。唐斯的建议当然很棒。我也会浏览really 规范和优化的库的源代码——特别是MapIntMapSetIntSet。除此之外,值得看看米兰的paper on his improvements to containers

【讨论】:

  • 还有 HashMap 和 HashSet,我的新宠。
  • @augustss:我同意,除非您浏览源代码,否则 HashMap 和 HashSet 就像 Milan 的纸质文档一样,是 Maps 和 Sets 的非常薄(但出色)的包装器。
  • @Don -- 哦,当然。我的脑筋急转弯。
  • 我在过去编写了applicativeTree 实现——sclv 是正确的,因为它很聪明。 =P 是的,它确实很好地概括了复制函数 O(log n),但这不是一个适用于大多数数据结构的技巧。
猜你喜欢
  • 1970-01-01
  • 2020-12-19
  • 2018-07-08
  • 2012-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-22
相关资源
最近更新 更多