我告诉我应该使用未装箱的载体进行繁重的科学计算(模拟运行数小时甚至数天),而不是列表,甚至是盒装载体。
答案 0 :(得分:13)
要理解的几件事。首先,盒装与未装箱:
如果您有一个"盒装"数字,基本上你有指向数字的指针。 (或者可能是一个指向未评估表达式的指针,如果它被评估,它将生成一个数字。)
如果你有一个" unboxed"数字,你有字面数字本身。
例如,在64位平台上,Char
是一个64位指针,指向Haskell堆上的某个位置,它可以任意保存32位Unicode代码点,或产生这种情况的大量无价值表达。另一方面,Char#
字面上只是一个32位整数。它可能在某个堆上,或者它可能在CPU寄存器或其他东西。
基本上Char#
是C所能想到的整数,而Char
是C所能想到的指向...实际上是一个数据结构,告诉你它是否是一个整数。评估与否,如果 评估,则整数在那里。这有点复杂。
需要注意的一件重要事情是盒装值可能未被评估;指针可以指向结果,也可以指向生成结果的表达式。 未装箱的值永远不会被取消评估。例如,32位整数不可能存储用于计算整数的任意大表达式;它只能存储整数本身。因此,盒装vs无盒装不可避免地与懒惰vs严格纠缠在一起。
懒惰评估可以让您避免计算实际上不需要的结果。 (无限列表等等。)但如果你实际上做总是需要所有结果,那么严格的评估实际上更快。
接下来,列出与矢量:
Haskell列表[Double]
(或其他)是指向双精度浮点数的单链接列表。每个浮动都可以不被评估,列表中的每个链接也可以不被评估。你可以想象,这没有任何缓存一致性!
向量是一个数组的东西。这意味着没有无限的向量;必须在创建时知道向量的大小。它还意味着要修改不可变向量,您必须复制整个事物,这在时间和空间上效率非常低。否则,您必须使用可变向量,否定函数式编程的一些好处。 另一方面,向量具有很棒的缓存一致性!
现在,盒装矢量基本上是指向实际数据的指针数组。未装箱的矢量是实际数据的数组。想猜猜哪些具有最佳缓存行为?作为一个副作用,一个未装箱的矢量也是严格的,如果你你需要整个矢量 - 会更快。
所以你看,未装箱的向量会给你一些限制,但可能会带来最佳性能。
刚才说了这么多,GHC执行各种棘手的优化,可以从根本上改变代码的性能,而不是它出现的#34;要做。 GHC可以将惰性代码转换为严格的代码,并且它可以执行" list fusion",其中循环遍历列表的函数链变为单个紧密循环。但是再一次,矢量操作的链条也融合了,所以......实际上,实际的表现取决于你想要做的事情。