在Numeric Haskell Repa Tutorial Wiki中,有一段读取(用于上下文):
10.1 Fusion,以及为什么需要它
Repa主要依靠阵列融合来实现快速代码。 Fusion是一个奇特的名字 GHC执行的内联和代码转换的组合 它编译你的程序。融合过程合并阵列填充 在Repa库中定义的循环,具有“worker”功能 你在自己的模块中写。如果融合过程失败,那么 结果程序将比它需要的慢很多,通常是10倍 使用普通Haskell列表减慢等效程序。在另一 手,提供融合工作,结果代码将运行速度快 相当干净的C程序。融合工作并不难 一旦你明白发生了什么。
我不明白的部分是:
“如果融合过程失败,那么 结果程序将比它需要的慢很多,通常是10倍 使用普通Haskell列表减慢等效程序。“
我理解为什么如果流融合失败会导致运行速度变慢,但为什么运行速度比列表慢得多?
谢谢!
答案 0 :(得分:9)
通常,因为列表是惰性的,并且Repa数组是严格的。
如果你没有融合懒惰列表遍历,例如
map f . map g
你支付 O(1)每个值的成本,以便在那里留下中间(懒惰)cons单元格。
如果您未能通过严格的序列融合相同的遍历,则每个值至少支付 O(n)以分配严格的中间数组。
此外,由于融合将您的代码转换为无法识别的Stream
数据类型,为了改进分析,您可能会留下具有太多构造函数和其他开销的代码。
答案 1 :(得分:3)
编辑:这是不正确的 - 请参阅Don Nelson的评论(以及他的答案 - 他对图书馆的了解远远超过我所知道的。)
不可变数组不能共享组件;无视融合,对不可变数组的任何修改都必须重新分配整个数组。相比之下,虽然列表操作是非破坏性的,但它们可以共享部分:f i (h:t) = i:t
,例如,通过创建一个新列表来替换列表的头部,其中第一个单元格指向第二个单元格。原始清单。此外,因为列表可以逐步构建,所以通过重复调用函数构建列表的生成器等函数仍然可以在O(n)时间内运行,而不融合的不可变数组上的等效函数需要重新分配数组。每次调用该函数,花费O(n ^ 2)时间。