4

在 Numeric Haskell Repa Tutorial Wiki中,有一段内容如下(上下文):

10.1 Fusion,以及为什么需要它

Repa 严重依赖数组融合来实现快速代码。Fusion 是 GHC 在编译程序时执行的内联和代码转换组合的一个奇特名称。融合过程将 Repa 库中定义的数组填充循环与您在自己的模块中编写的“worker”函数合并。如果融合过程失败,那么生成的程序将比它需要的慢得多,通常比使用普通 Haskell 列表的等效程序慢 10 倍。另一方面,如果融合工作正常,生成的代码将与等效的干净编写的 C 程序一样快地运行。一旦你了解了发生了什么,使融合工作并不难。

我不明白的部分是这样的:

“如果融合过程失败,那么生成的程序将比它需要的慢得多,通常比使用普通 Haskell 列表的等效程序慢 10 倍。”

我明白为什么如果流融合失败它会运行得更慢,但为什么它的运行速度比列表慢得多?

谢谢!

4

2 回答 2

9

通常,因为列表是惰性的,而 Repa 数组是严格的。

如果您未能融合惰性列表遍历,例如

map f . map g

您为将中间(惰性)cons 单元留在那里而支付每个值的O(1)成本。

如果您未能在严格序列上融合相同的遍历,则您为分配严格的中间数组每个值至少支付O(n) 。

此外,由于 fusion 将您的代码破坏为无法识别的Stream数据类型,为了改进分析,您可能会留下具有太多构造函数和其他开销的代码。

于 2012-12-03T20:39:20.373 回答
3

编辑:这是不正确的——请参阅 Don Nelson 的评论(以及他的回答——他对图书馆的了解比我多得多)。

不可变数组不能共享组件;不考虑融合,对不可变数组的任何修改都必须重新分配整个数组。相比之下,虽然列表操作是非破坏性的,但它们可以共享部分:f i (h:t) = i:t例如,通过创建一个新列表以恒定时间替换列表的头部,其中第一个单元格指向原始列表的第二个单元格。此外,由于列表可以增量构建,因此通过重复调用函数来构建列表的生成器等函数仍然可以在 O(n) 时间内运行,而在没有融合的不可变数组上的等效函数需要重新分配数组每次调用函数,花费 O(n^2) 时间。

于 2012-12-03T20:39:59.337 回答