Functional programming 我应该如何在OCaml中仅使用不可变的东西对1000亿项进行排序?
好的,假设我们有Functional programming 我应该如何在OCaml中仅使用不可变的东西对1000亿项进行排序?,functional-programming,ocaml,Functional Programming,Ocaml,好的,假设我们有1000亿项需要排序 我们的内存足够存储这些项目 我们仍然可以使用List.sort(合并排序)对它们进行排序吗 我关注的问题有两部分: 在这种情况下,mergesort需要的额外空间会成为一个问题吗 由于我们使用不可变的数据结构,我们必须在排序期间为1000亿项重复创建新列表,这会成为一个缺点吗?在性能方面 对于排序1000亿项,在这种情况下我是否应该使用array?最聪明的做法是不重新分配太多内存(开始时一分为二不会分配新内存)。给定nconses的输入列表,它将在最坏的情况
1000亿
项需要排序
我们的内存足够存储这些项目
我们仍然可以使用List.sort(合并排序)对它们进行排序吗
我关注的问题有两部分:
mergesort
需要的额外空间会成为一个问题吗1000亿
项重复创建新列表,这会成为一个缺点吗?在性能方面1000亿
项,在这种情况下我是否应该使用array
?最聪明的做法是不重新分配太多内存(开始时一分为二不会分配新内存)。给定n
conses的输入列表,它将在最坏的情况下分配n*log(n)
list conses(基本相同的最佳情况)。考虑到元素本身的值将在输入、中间和输出列表之间共享,您将只按列表cons分配3个单词,这意味着排序将在内存中总共分配3*n*log(n)
单词(对于n=1000亿
,3*log(n)
是110
,这是一个非常大的常数)
另一方面,垃圾收集可以收集一些内存:最坏的内存使用情况是总的活动内存,而不是总的分配内存。事实上,在递归子小区的log(n)
层中构建的中间列表可以在返回任何结果之前收集(它们以与最终merge
分配新小区相同的速率死亡),因此该算法在最坏的情况下保持n
额外的活动cons小区,这意味着只有3*n
字或24*n
字节。对于n=1000亿
,这意味着额外增加了2.4 TB的内存,与您首先存储输入列表脊椎所需的内存相同
最后,如果您没有保留对输入列表本身的引用,则可以在对其排序后立即收集前半部分,为您提供一个
n/2
最坏情况界限,而不是n
。在对前半部分进行排序时,您可以收集前半部分的前半部分,从而得到n/4
最坏情况界限,而不是n/2
。这种推理的局限性在于,我相信有了足够的GC工作,您实际上可以对列表进行完全适当的排序——将一些固定大小的内存池模块化为停止复制第一代GC,其大小将影响算法的时间性能。“我们的内存足够容纳这些项目。”你有一台有800 GB内存的计算机吗?@PascalCuoq:据说MPI的人有TB内存的计算机。你不能把这些数据分成两半,分别排序,然后合并结果吗?(只是KIDDIN…)@ PascalCuoq很好地把这个问题看作是在<代码>爱丽丝的仙境中存在的。“如果你不引用输入列表本身”,这将需要特别的小心。默认情况下,如果函数是编写的,let rec mergesort l=…
,l
在调用返回之前处于活动状态,即使mergesort
中途停止引用l
。例如,函数mergesort
需要对列表进行引用,并在处理列表时将该引用设置为包含[]
。这样的技巧将允许在下一个周期结束时恢复原始列表。尾部调用从堆栈中删除参数,从而允许恢复它们。我在编写消息时检查了这个问题。merge
函数总是在尾部位置调用,因此我认为在返回列表之前释放与子层对应的内存是正确的(以相同的速率merge
分配新单元)。既然您坚持这样做,我认为当前的实现不允许出于这个原因收集l
的前半部分,所以我的消息的最后部分可能不存在。我会问达米恩,也许会提出一个补丁。