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
    的前半部分,所以我的消息的最后部分可能不存在。我会问达米恩,也许会提出一个补丁。