好的,假设我们有100 billion
需要排序的项目。
我们的内存足以容纳这些项目。
我们还可以使用 List.sort(合并排序)对它们进行排序吗?
我的担忧有两个部分:
- 在这种情况下,需要的额外空间是否会
mergesort
成为问题? - 由于我们使用不可变的数据结构,在排序过程中我们必须重复为
100 billion
项目创建新的列表,这会成为一个缺点吗?在性能方面?
对于排序100 billion
项目,我应该array
在这种情况下使用吗?
好的,假设我们有100 billion
需要排序的项目。
我们的内存足以容纳这些项目。
我们还可以使用 List.sort(合并排序)对它们进行排序吗?
我的担忧有两个部分:
mergesort
成为问题?100 billion
项目创建新的列表,这会成为一个缺点吗?在性能方面?对于排序100 billion
项目,我应该array
在这种情况下使用吗?
标准的合并排序实现很聪明,不会重新分配太多内存(一开始的分成两半不会分配新内存)。给定一个输入的n
conses 列表,它将在最坏的情况下分配n * log(n)
列表 conses(具有基本相同的最佳情况)。鉴于元素本身的值将在输入、中间和输出列表之间共享,因此您将仅通过列表 cons 分配 3 个单词,这意味着该排序将3 * n * log(n)
总共分配内存中的单词(对于n = 100 billion
, 3 * log(n)
is 110
,这是相当一个巨大的常数因子)。
另一方面,垃圾收集可以收集一些内存:最坏情况下的内存使用是总的活动内存,而不是总分配的内存。事实上,在log(n)
递归子调用层中构建的中间列表可以在返回任何结果之前收集(它们以与最终merge
分配新单元格相同的速率死亡),因此该算法n
在最坏的情况下保留额外的活动 cons 单元格,这仅表示3*n
字或24*n
字节。对于n = 100 billion
,这意味着 2.4 TB 的额外内存,与您最初存储输入列表的脊椎所需的内存一样多。
最后,如果您不保留对输入列表本身的引用,则可以在排序后立即收集它的前半部分,从而为您提供n/2
最坏情况的界限而不是n
. 并且您可以在对前半部分进行排序时收集前半部分的前半部分,从而为您提供n/4
最坏情况的界限,而不是n/2
. 以这种推理达到极限,我相信通过足够的 GC 工作,您实际上可以完全对列表进行排序 - 为 stop© 第一代 GC 模一些恒定大小的内存池,其大小将影响时间性能算法。