Haskell 使用数据计算不同的值。映射泄漏内存

Haskell 使用数据计算不同的值。映射泄漏内存,haskell,memory-leaks,map,io,Haskell,Memory Leaks,Map,Io,以下程序在计算250 MB文件中的不同行长度时使用100+MB RAM。如何修复它以使用更少的内存?我想我在值中误用了惰性IO、foldr和Data.Map的惰性 导入控件。应用程序 导入符合条件的数据。映射为M 导入数据。列表 main=do 内容降低最大驻留时间的一种方法是使用IntMap而不是Map,后者是Int键的Map数据结构的专用版本。这是一个简单的改变: 导入控件。应用程序 导入符合条件的Data.IntMap作为I 导入数据。列表 main=do 内容第一个大错误 main =

以下程序在计算250 MB文件中的不同行长度时使用100+MB RAM。如何修复它以使用更少的内存?我想我在值中误用了惰性IO、
foldr
Data.Map
的惰性

导入控件。应用程序
导入符合条件的数据。映射为M
导入数据。列表
main=do

内容降低最大驻留时间的一种方法是使用
IntMap
而不是
Map
,后者是
Int
键的
Map
数据结构的专用版本。这是一个简单的改变:

导入控件。应用程序
导入符合条件的Data.IntMap作为I
导入数据。列表
main=do

内容第一个大错误

main = do
  content <- readFile "output.csv"
  print $ (foldr count M.empty . map length . lines) content

count a b = M.insertWith (+) a 1 b
遍历构成thunk的整个行列表-由于懒惰,当时甚至没有计算
长度
调用-然后从右向左计算。因此,除了用于构建
映射的thunk之外,整个文件内容都在内存中

如果你从一个事物列表中构建一个地图,总是使用严格的左折叠(如果列表很短,而事物不是很大,则无所谓),除非语义要求右折叠(如果使用非交换函数组合值,可能会出现这种情况,但即使如此,在构建地图之前,通常还是最好使用左折叠并反转列表)

Data.Map
s(或
Data.IntMap
s)是严格的,这使得在遍历整个列表之前无法生成部分输出,因此这里不能使用
foldr
的优点

下一个(可能的)问题是(同样是惰性),当将映射到的值放入
映射时,您不计算这些值,因此如果有一个特别经常出现的行长度,该值将成为一个巨大的冲击

((...((1+1)+1)...+1)+1)
成功

main = do
  content <- readFile "output.csv"
  print $ (foldl' count M.empty . map length . lines) content

count mp a = M.insertWith' (+) a 1 mp

使用
insertWith
(如果没有素数,
Data.Map.Strict
模块总是计算放入映射中的值)离开
count

您可以尝试导入Data.Map.Strict而不是Data.Map.I想要O(1)空间复杂性这甚至是不可能的。你需要至少在不同的行长度的数量上保持内存的线性。一切都是O(1)给定上界。假设这种情况是没有用的。我假设最常见的情况——行长度上界,而不是行数上界。我认为这个问题不值得争论——通常说二进制搜索有O(logn)尽管每次比较都很复杂,但它可以以任意方式取决于项目大小。谢谢。使用
Data.Map.Strict
insertWith'
我可以获得所需的内存行为。但是现在它比Perl慢。我应该使用ST吗?“但是现在它慢了”这是我可以做的最小示例。我需要类似
awk的东西{print$5$25}| sort-c
最终,因此
IntMap
对我的整个程序不起作用。在改为左折叠后,它并没有变慢,我只是希望它比等效的Perl程序快,但我很失望。我不知道
awk{print$5$25}
有什么作用。[好吧,它打印了一些东西,这很清楚;]当然,有一件事会减慢它的速度,那就是你在文件中读取的是
Char
s的链表,这意味着必须对字节进行解码,建立并解构一个链表,…如果文件都是ASCII,
Data.ByteString.Lazy.Char8
将是你速度最好的朋友。如果不是这样,
Data.Text.IO
可能值得一试试试看。我只需要ASCII码,所以我尝试了使用导管的测试环。同样的情况,所以我认为
Data.Map
有问题。
main = do
  content <- readFile "output.csv"
  print $ (foldl' count M.empty . map length . lines) content

count mp a = M.insertWith' (+) a 1 mp
import Data.Map.Strict