Haskell程序的奇怪空间行为

时间:2011-08-23 16:20:37

标签: performance haskell

我认为Cont monad只相当于CPS Transformation,所以如果我有 一个monadic sum,如果我在Identity monad中运行,它将因堆栈溢出而失败,如果 我在Cont Monad中运行它,由于尾递归会没问题。

所以我写了一个简单的程序来验证我的想法。但令我惊讶的是,由于我的知识有限,结果是不合理的。

所有程序均使用ghc --make Test.hs -o test && ./test

编译
sum0 n = if n==0  then  0  else n + sum0 (n-1)
sum1 n = if  n==0  then return 0 else sum1 (n-1) >>= \ v ->  seq v (return (n+v))
sum2 n k = if n == 0 then k 0 else sum2 n (\v -> k (n + v))
sum3 n k = if n == 0 then k 0 else sum3 n (\ !v -> k (n + v))
sum4 n k = if n == 0 then k 0 else sum4 n (\ v -> seq v ( k (n + v)))
sum5 n = if  n==0  then return 0 else sum5 (n-1) >>= \ v ->   (return (n+v)) 
  • main = print (sum0 3000000)
    堆栈溢出。这是合理的。

  • main = print (flip runCont id (sum1 3000000))
    使用180M内存,这是合理的,但我不清楚为什么seq需要这里,因为在n变为0之前它的继续不适用。

  • main = print (flip runCont id (sum5 3000000))
    堆栈溢出。为什么?

  • main = print (flip runCont (const 0) (sum1 3000000))
    使用130M内存。这是合理的。

  • main = print (flip runCont (const 0) (sum5 3000000))
    使用118M内存。这是合理的。

  • main = print (sum2 3000000 (const 0))
    使用大量内存(超过1G)。我认为sum2相当于sum5(当sum5位于Cont monad时)。为什么呢?

  • main = print (sum3 3000000 (const 0))
    使用大量内存。我认为sum3相当于sum1Cont monad)。为什么?

  • main = print (runIdentity (sum1 3000000))
    堆栈溢出,正是我想要的。

  • main = print (sum3 3000000 id)
    使用大量内存。相当于sum1,为什么?

  • main = print (sum4 3000000 id)
    使用大量内存。相当于sum1,为什么?

  • main = print (sum [1 .. 3000000])
    堆栈溢出。 sum = foldl (+) 0的来源,所以这是合理的。

  • main = print (foldl' (+) 0 [1 .. 3000000])
    使用1.5M。

1 个答案:

答案 0 :(得分:3)

首先,我认为sum2sum3sum4从未真正减少n。所以他们使用了大量的内存,因为他们正在进入一个无限循环来进行分配。

在纠正之后,我再次使用以下结果运行每个测试,其中“分配”指的是近似峰值内存使用:

  • main = print (sum0 3000000):在分配很少的内存后,堆栈溢出
  • main = print (flip runCont id (sum1 3000000)):成功,分配类似于您所看到的金额
  • main = print (flip runCont id (sum5 3000000)):在将类似的内存量分配为sum1后,堆栈溢出。
  • main = print (flip runCont (const 0) (sum1 3000000)):成功,与上述类似的分配
  • main = print (flip runCont (const 0) (sum5 3000000)):相同
  • main = print (sum2 3000000 (const 0)):成功,约为sum1
  • 的70%
  • main = print (sum3 3000000 (const 0)):成功,约为sum1
  • 的50%
  • main = print (runIdentity (sum1 3000000)):堆栈溢出,分配很少
  • main = print (sum3 3000000 id):成功,约为sum1
  • 的50%
  • main = print (sum4 3000000 id):成功,约为sum1
  • 的50%
  • main = print (sum [1 .. 3000000]):堆栈溢出,分配约为sum1
  • 的80%
  • main = print (foldl' (+) 0 [1 .. 3000000]):成功,几乎没有分配

这主要是您所期望的,除了seqsum1sum5之间产生差异的原因除外。