【问题标题】:Do linked lists have a practical performance advantage in eager functional languages?链表在急切的函数式语言中是否具有实际的性能优势?
【发布时间】:2023-03-04 10:26:01
【问题描述】:

我知道,在惰性函数式语言中,链表采用生成器式语义,并且在优化编译器下,当它们实际上不用于存储时,它们的开销可以完全消除。

但是在热切的函数式语言中,它们的使用似乎同样繁重,而优化它们似乎更加困难。像 Scheme 这样的语言在平面数组上使用它们作为主要序列表示是否有很好的性能原因?

我知道使用单链表会带来明显的时间复杂度影响;我更多地考虑使用急切的单链表作为主要序列表示的实际性能后果,考虑了编译器优化。

【问题讨论】:

  • 1) 简单 2) 琐碎的 O(1) 前置 3) 共享
  • @Bergi 简单并不是真正的“性能”优势,但它是一个原因。不过,我不太确定我是否理解您的分享观点。假设不变性,您可以轻松共享平面数组。
  • 但您不能共享部分数组。列表可以共享它们的尾巴。
  • @Bergi 共享是否发生在急切的语言环境中?我想你可以手动完成。
  • @DavidYoung 我的意思不是共享惰性计算,而是共享数据结构组件。我猜这在每一种具有不可变数据结构的语言中都是如此,无论是懒惰还是渴望。

标签: performance haskell linked-list functional-programming scheme


【解决方案1】:

TL; DR:没有性能优势!

第一个 Lisp 将cons(链表单元格)作为唯一的数据结构,并将它用于所有事情。如今,Common Lisp 和 Scheme 都有向量,但对于函数式风格,它并不是一个很好的匹配。链表可以有一个递归步骤,在累加器前面添加零个或多个元素,最后你有一个在迭代之间共享的列表。该操作可能会进行不止一次递归,从而使多个版本都共享尾部。我想说共享是链表最重要的方面。如果您制作一个极小极大算法并将状态存储在一个链表中,您可以更改状态,而无需复制状态中未更改的部分。

C++ 的创建者 Bjarne Stroustrup 提到 in a talk,即使您按顺序插入并需要移动数据结构中的一半元素,将数据结构打乱并像在链表中一样大小加倍的惩罚也很容易胜过.请记住,这些是双向链表和变异插入,但他提到大部分时间都是线性跟踪指针,获取所有 CPU 缓存未命中,以找到正确的位置,因此对于排序中的每个 O(n) 搜索列出一个向量会更好。

如果你有一个程序,你在一个不在前面的列表上做很多插入,那么也许一棵树是一个更好的选择,你可以在 CL 和 Scheme 中用cons 来做。事实上,所有 Chris Okasaki 纯函数数据结构都可以用cons 实现。 Haskell 中的大多数“可变”结构的实现与它类似。

如果您在 Scheme 中遇到性能问题,并且在分析后发现您应该尝试用数组替换链表操作,那么没有什么可以阻止的。最后,所有算法选择都有利有弊。硬计算在任何语言中都很难。

【讨论】:

  • 应该提到的是,C++ 数组/向量之所以这么快,是因为它们默认是 unboxed(内存压缩)。这允许在没有缓存未命中的情况下实际访问 元素。但是在动态语言(包括有限的 OO 动态类型)以及 Haskell 等人中,您通常只能将数组作为 指针数组,这意味着如果需要,您仍然会遇到缓存未命中在 O (n) 搜索中查找元素。数组仍然会比链表快,但不会像在 C++ 中那样是一个很大的因素。
  • @leftaroundabout 我同意,但我们现在在 Haskell 中确实有未装箱的向量。这些必须使用低级不安全原语来定义,并且仅适用于不可装箱的类型,但对于这些类型,性能至少应该更接近 C++。
  • @leftaroundabout 不,速度主要是通过去除内存依赖获得的;指针向量仍然比值的链接列表快得多。取消引用内存的潜在性非常高,但吞吐量并不高。
  • @Veedrac 仅在独立于其他元素处理每个元素时为您提供帮助,并且仅当编译器和处理器可以很好地重新排序指令以致延迟完全无序时才会帮助您。在实践中,现在这通常工作得很好,但仍然没有完全避免大多数导致延迟的缓存未命中时那么快。
  • @leftaroundabout OoO 即使在处理不是独立的情况下也会使这项工作,只要它大致线性地遍历即可;循环最终将流水线化,延迟将被隐藏。显然,拥有连续数据会更好,但相对而言重要性较低。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-11
  • 2011-12-18
  • 2011-07-02
  • 2016-03-14
  • 1970-01-01
  • 2015-09-10
相关资源
最近更新 更多