仅在 GHC 解释器执行以下操作时在某些情况下发生空间泄漏: concat; !! n
我定义了我自己的 concat
版本,myConcat
:
module Eh where
myConcat [] = []
myConcat ([]:os) = myConcat os
myConcat ((x:xs):os) = x : myConcat (xs:os)
(!!!) :: [a] -> Int -> a
xs !!! n | n < 0 = error "negative index"
[] !!! _ = error "index too large"
(x:_) !!! 0 = x
(_:xs) !!! n = xs !!! (n-1)
如果我执行 myConcat
Eh
加载为“已解释”,在加载之前我不会对其进行编译。
code run in the GHC interpreter space leak? myConcat (repeat [1,2,3,4]) !! (10^8) Yes concat (repeat [1,2,3,4]) !! (10^8) No myConcat (repeat [1,2,3,4]) !!! (10^8) No concat (repeat [1,2,3,4]) !!! (10^8) No
现在,如果我编译 Eh
(ghc --make -O2 Eh.hs
),然后将其加载到解释器中并重新运行这些测试,则不会出现空间泄漏。如果我编译每个测试用例而不是在解释器中运行它们,情况也是如此。
这是怎么回事?
我正在运行 GHC 6.12.3。
I define my own version of concat
, myConcat
:
module Eh where
myConcat [] = []
myConcat ([]:os) = myConcat os
myConcat ((x:xs):os) = x : myConcat (xs:os)
(!!!) :: [a] -> Int -> a
xs !!! n | n < 0 = error "negative index"
[] !!! _ = error "index too large"
(x:_) !!! 0 = x
(_:xs) !!! n = xs !!! (n-1)
If I do myConcat <some huge list> !! n
in the GHC interpreter, it steals my memory at 300MB/s, and I have to kill it before it can summon the OOM killer. Note here that I load Eh
as "interpreted", I don't compile it before loading it.
code run in the GHC interpreter space leak? myConcat (repeat [1,2,3,4]) !! (10^8) Yes concat (repeat [1,2,3,4]) !! (10^8) No myConcat (repeat [1,2,3,4]) !!! (10^8) No concat (repeat [1,2,3,4]) !!! (10^8) No
Now if I compile Eh
(ghc --make -O2 Eh.hs
), and then load it in the interpreter and re-run these tests, none of them space leak. Same if I compile each test case instead of running them in the interpreter.
What's going on?
I'm running GHC 6.12.3.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这里的问题是严格性。 Haskell 中的计算是不严格的,因此通常仅在确实需要结果时才执行计算。相反,会创建一个所谓的 thunk 来表示尚未执行的计算。
然而,在某些情况下,编译器可以检测到无论如何都需要计算结果,因此用实际计算替换 thunk 的创建。
Haskell Wiki 可能对此有更好的解释。
要修复您的
myConcat
函数,您必须确保它不会通过手动强制严格评估来创建数百万个重击。一种(看起来不太漂亮)的方法可能是:The issue here is strictness. Evaluation in Haskell is non-strict, so computations are usually performed only if their results are really needed. Instead, a so-called thunk is created that represents the not-yet-performed computation.
In certain cases the compiler can however detect that the result of the computation will be needed anyway and therefore replaces the creation of thunks by the actual computation.
The Haskell Wiki probably explains this better.
To fix your
myConcat
function you have to make sure it doesn't create millions of thunks by manually forcing strict evaluation. One (not very pretty looking) way of doing this could be: