以下程序在计算250 MB文件中的不同行长度时使用100+ MB RAM。如何修复它以减少使用RAM?我想我错误地使用了惰性IO,foldr
和Data.Map
的懒惰值。
import Control.Applicative
import qualified Data.Map as M
import Data.List
main = do
content <- readFile "output.csv"
print $ (foldr count M.empty . map length . lines) content
count a b = M.insertWith (+) a 1 b
答案 0 :(得分:5)
中的第一个大错误
main = do
content <- readFile "output.csv"
print $ (foldr count M.empty . map length . lines) content
count a b = M.insertWith (+) a 1 b
正在使用foldr
。这构造了表单
length firstLine `count` length secondLine `count` ... `count` length lastLine `count` M.empty
遍历构成thunk的整个行列表 - 此时甚至不会因为懒惰而评估length
调用 - 然后从右到左进行评估。因此,除了用于构建Map
。
如果你从一系列事物中建立一个地图,总是使用一个严格的左折(好吧,如果列表很短,而且事情不是很大,那没关系)除非语义需要一个正确的折叠(如果您使用非交换函数组合值,可能就是这种情况,但即使这样,在构建映射之前,通常最好使用左侧折叠和reverse
列表)。
Data.Map
s(或Data.IntMap
s)是严格的,只有这样才能在遍历整个列表之前生成部分输出,因此foldr
的强度不能在这里使用。
下一个(可能的)问题是(再次懒惰)当你将它们放入Map
时你没有评估映射到的值,所以如果有一个特别经常出现的行长度,那么这个值成为一个巨大的蠢货
((...((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
这样一旦读入行就可以对行进行垃圾收集,并且不会在值中积累任何行。这样你就不会一次在内存中需要多行文件,甚至不需要完全在内存中,因为length
在Map
中被记录之前会被评估。
如果您的containers
套餐足够新,您也可以
import Data.Map.Strict
并使用count
离开insertWith
(没有素数,Data.Map.Strict
模块始终评估放入地图的值。)
答案 1 :(得分:0)
降低最大停留时间的一种方法是使用IntMap
而不是Map
,这是Map
密钥的Int
数据结构的专用版本。这是一个简单的改变:
import Control.Applicative
import qualified Data.IntMap as I
import Data.List
main = do
content <- readFile "output.csv"
print $ (foldr count I.empty . map length . lines) content
count a b = I.insertWith (+) a 1 b
使用/usr/share/dict/words
将此版本与您的版本进行比较,发现最大驻留时间从大约100MB到60MB。请注意,这也没有任何优化标志。如果你发动这些,最大居住率很可能会进一步改善。