如何避免Haskell空间泄漏? [关闭]

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何避免Haskell空间泄漏? [关闭]相关的知识,希望对你有一定的参考价值。

Context

我不久前开始通过解决今年的代码问世任务来开始学习Haskell。

在解决Part 2 of Day 17时,我遇到了令人讨厌的内存泄漏(空间泄漏) - 我想。

(这是完整的README,包括第2部分,只有在解决第1部分后才能访问它。)

我的解决方案工作正常,但只有一个脏的小黑客,这迫使Haskell不时地评估一个中间计算。

我在每5000次迭代后使用traceShow将中间状态打印到控制台(请参阅此处the actual code)。这样程序在合理的时间内完成,并且不会使用太多内存。

问题:如果我删除它(根本不打印中间状态,只打印最后一个状态),程序将占用所有可用内存。 :(

我开始使用iterate,然后我读到使用它会导致像我注意到的东西。我已经取代了它。没有。试过不同的褶皱(foldlfoldl'等)。没有。我不确定在这一点上可能导致这种情况的原因虽然我的猜测是在某些时候还有一些不那么明显的懒惰评估正在进行中。

我的问题:我怎么能避免这个?是什么导致我的情况?

感谢您的时间和见解。哦,我很确定这个问题有更短,更甜的解决方案,但目前我只对导致内存泄漏的原因感兴趣。

Testing

我已经隔离了我注意到这个错误的代码部分。

type Count = Int
type StepSize = Int
type Value = Int
type Position = Int
type Size = Int

data Buffer = Buffer Size Position (Maybe Value)
  deriving Show

data Spinlock = Spinlock StepSize !Position !Value Buffer
  deriving Show

updateBuffer :: Position -> Value -> Buffer -> Buffer
updateBuffer position value (Buffer size trackedPosition trackedValue)
  | position == trackedPosition = Buffer nextSize trackedPosition (Just value)
  | otherwise = Buffer nextSize trackedPosition trackedValue
  where nextSize = size + 1

stepSpinlock :: Count -> Spinlock -> Spinlock
stepSpinlock count spinlock@(Spinlock stepSize position value buffer)
  | count == 0 = spinlock
  | otherwise = stepSpinlock nextCount newSpinlock
  where (Buffer size _ _) = buffer
        nextCount = count - 1
        nextPosition = ((position + stepSize) `mod` size) + 1
        nextValue = value + 1
        newBuffer = updateBuffer nextPosition nextValue buffer
        newSpinlock = Spinlock stepSize nextPosition nextValue newBuffer

main = do
  let stepSize = 371
      buffer = Buffer 1 0 Nothing
      spinlock = Spinlock stepSize 0 0 buffer
      (Spinlock _ _ _ (Buffer _ _ (Just value))) = stepSpinlock (50000000 - 1) spinlock
  print $ value 

我用stacklts-10.1),GHC 8.2.2来运行它。

运行这会占用我所有的内存并且在一段时间后无法分配内存错误而失败。

如果我替换它

  | otherwise = stepSpinlock nextCount newSpinlock

有了这个

  | otherwise = stepSpinlock nextCount $ if count `mod` 5000 == 0
                                         then traceShow newSpinlock newSpinlock
                                         else newSpinlock 

它运行在合理的时间。然后用stack ghc Part2.hs重新编译然后再次运行./Part2 < input.txt

答案

以下作品:

{-# language BangPatterns #-}

...

stepSpinlock :: Count -> Spinlock -> Spinlock
stepSpinlock count spinlock@(Spinlock !stepSize !position !value !buffer)
...

在每次迭代中,您更新valuebuffer而不对它们做任何事情,因此thunk积累。或者,我建议只使用{-# language Strict #-}。我还注意到在使用CircularBuffer的程序运行期间根本没有使用input.txt

以上是关于如何避免Haskell空间泄漏? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在 Haskell 中 Floyd-Warshall 的表现——修复空间泄漏

避免android片段中内存泄漏的最佳方法是啥

如何避免内部类中的内存泄漏

如何避免内存泄漏溢出

如何使用模块化代码片段中的LeakCanary检测内存泄漏?

ThreadLocal内存泄露原因,如何避免