我找到了关于空间泄漏的haskell wiki页面,它声称列出了真实世界泄漏的例子,但它没有,它并没有真正说明空间泄漏是什么,它只是链接到页面以查找内存泄漏。
什么是太空泄漏?
发布于 2017-09-01 23:17:54
正如@Rasko的答案所指出的,空格泄漏是指程序或特定计算使用的内存比计算和/或程序员期望的内存更多(通常更多)的情况。
Haskell程序往往特别容易受到空间泄漏的影响,这主要是因为懒惰的评估模型(有时由于IO与该模型的交互方式而变得复杂)和语言的高度抽象性,这使得程序员很难准确确定如何执行特定的计算。
考虑一个具体的例子是有帮助的。这个Haskell程序:
main = print $ sum [1..1000000000]是一种惯用的方法,用来对第一个10亿个整数求和。它用-O2编译,在几秒钟内以恒定内存运行(几兆字节,基本上是运行时开销)。
现在,任何一位程序员都会期望一个程序能够在不消耗内存的情况下运行前十亿个整数,但是这个Haskell版本表现良好是有点令人惊讶的。毕竟,从字面上看,它在对10亿个整数进行求和之前,会构造一个包含10亿个整数的列表,因此应该至少需要几个千兆字节(仅用于存储10亿个整数,更不用说Haskell链接列表的开销)。
然而,惰性评估确保列表只在需要时生成,同样重要的是,编译器执行的优化确保在将列表元素添加到累加和时,程序确认不再需要列表元素,并允许垃圾收集它们,而不是一直保存到计算结束。因此,在计算过程中的任何时候,只需要在内存中保存到列表中间的滑动“窗口”--较早的元素已经被丢弃,而稍后的元素尚未被延迟计算。(实际上,优化比这更深入:甚至没有构建列表,但这一点对程序员来说并不明显。)
哇哦..。Haskell程序员已经习惯了这样一种想法:在巨大(甚至无限)的数据结构中“只需”使用他们所需要的内存就可以自动计算。
但是,对程序做一个小改动,比如打印列表的长度,以证明我们正在做的所有艰苦工作:
main = let vals = [1..1000000000]
in print (sum vals, length vals)突然间,空间使用量激增到几十千兆字节(或者在我的笔记本电脑里,在它开始无可救药地交换之前,它会爆炸到大约13千兆字节,我就会把它杀死)。
这是空间泄漏。计算这个列表的总和和长度显然是可以在常量空间内使用列表中的“滑动窗口”视图来完成的,但是上面的程序使用的内存比需要的要多得多。其原因是,一旦列表被命名为vals (在两处使用),编译器就不再允许立即丢弃“已使用”元素。如果首先对sum vals进行评估,则会懒洋洋地生成和总结列表,但整个庞大的列表将一直保存到可以计算length vals为止。
作为一个更实际的例子,您可以编写一个简单的程序来计算文件中的单词和字符:
main = do txt <- getContents
print (length txt, length (words txt))这在高达几兆字节的小测试文件上工作得很好,但在10 all文件上却非常缓慢,如果您试图在一个100 all文件上运行它,它将缓慢但肯定地占用所有可用内存。同样,问题是--尽管文件内容被懒洋洋地读取到txt中--因为txt被使用了两次,当计算length txt时,整个内容以Haskell String类型(内存-大文本块的低效表示)的形式读取到内存中,在计算完length (words txt)之前,不能释放任何内存。
请注意:
main = do txt <- getContents
print $ length txt以及:
main = do txt <- getContents
print $ length (words txt)它们都在恒定的空间中快速运行,即使在大文件上也是如此。
另外,修复上述空格漏洞通常需要重写计算,以便通过内容一次计数字符和单词,这样编译器就可以确定已经处理的文件的内容在计算结束之前不需要保存在内存中。一个可能的解决办法是:
{-# LANGUAGE BangPatterns #-}
import Data.List
import Data.Char
charsWords :: String -> (Int, Int)
charsWords str = let (_, chrs, wrds) = foldl' step (False, 0, 0) str
in (chrs, wrds)
where step (inWord, cs, ws) c =
let !cs' = succ cs
!ws' = if not inWord && inWord' then succ ws else ws
!inWord' = not (isSpace c)
in (inWord', cs', ws')
main = do txt <- getContents
print $ charsWords txt这个解决方案的复杂性(使用!模式和显式折叠而不是length和words)说明了空间泄漏是多么的困难,特别是对于新的Haskell程序员。使用foldl'而不是foldl一点也不明显(但使用foldr或foldr'将是一场灾难!),cs'和ws'之前的刘海对于避免空间泄漏至关重要,但inWord'之前的爆炸(虽然稍微提高了性能),等等。
发布于 2017-09-01 20:49:06
当计算机程序使用超出需要的内存时,就会发生空间泄漏。与内存泄漏不同的是,泄漏的内存从未被释放过,空间泄漏占用的内存会被释放,但比预期的要晚。 Source
https://stackoverflow.com/questions/46007746
复制相似问题