为了提高效率,ghc是否只将一次使用的列表转换为生成器?

时间:2011-11-26 11:22:47

标签: list optimization haskell generator lazy-evaluation

如果是这样,这是标准的一部分还是我们可以依赖的ghc特定优化?或者只是我们不一定依赖的优化。

P.S .: 当我尝试一个测试样本时,它似乎表明它正在发生/

Prelude> let isOdd x = x `mod` 2 == 1
Prelude> let isEven x = x `mod` 2 == 0
Prelude> ((filter isOdd).(filter isEven)) [1..]

咀嚼CPU,但不会占用太多内存。

2 个答案:

答案 0 :(得分:7)

取决于你对发电机的意思。该列表是懒惰生成的,由于没有其他任何内容引用它,消耗的部分几乎立即被垃圾收集。由于上述计算的结果没有增长,整个计算在恒定的空间中运行。这不是标准规定的,但是因为在这个例子中实现具有不同空间行为的非严格语义(并且模糊地相似),实际上你可以依赖它。

但通常情况下,列表仍然是作为列表生成的,因此产生了大量垃圾。在有利的情况下,ghc会删除列表[1 .. ]并生成一个非分配循环:

result :: [Int]
result = filter odd . filter even $ [1 .. ]

(使用懒惰的Prelude函数),使用-O2编译生成核心

List.result_go =
  \ (x_ayH :: GHC.Prim.Int#) ->
    case GHC.Prim.remInt# x_ayH 2 of _ {
      __DEFAULT ->
        case x_ayH of wild_Xa {
          __DEFAULT -> List.result_go (GHC.Prim.+# wild_Xa 1);
          9223372036854775807 -> GHC.Types.[] @ GHC.Types.Int
        };
      0 ->
        case x_ayH of wild_Xa {
          __DEFAULT -> List.result_go (GHC.Prim.+# wild_Xa 1);
          9223372036854775807 -> GHC.Types.[] @ GHC.Types.Int
        }
    }

一个普通的循环,从1运行到maxBound :: Int,在路上不产生任何东西,最后产生[]。 简单回归[]几乎足够聪明。注意,只有一个除以2,GHC知道如果Int是偶数,则它不能是奇数,因此检查已被消除,并且在没有分支的情况下创建非空列表(即,编译器已消除了无法访问的分支。)

答案 1 :(得分:2)

严格地说,Haskell没有指定任何特定的评估模型,因此实现可以自由地实现语言的语义。但是,在任何理智的实现中,包括GHC,您都可以依赖于在恒定空间中运行。

在GHC中,像这样的计算导致单个链接列表以thunk结尾,表示尚未评估的列表的剩余部分。在评估此列表时,将根据需要生成更多列表,但由于列表的开头未在其他任何地方引用,因此较早的部分立即有资格进行垃圾回收,因此您将获得恒定的空间行为。

启用优化后,GHC很可能会在此处执行砍伐森林,根本不需要拥有列表,结果将是一个没有执行分配的简单循环。

相关问题