输入文件由两行组成,每行包含多个数字
1 2 3 4...
5 6 7 8...
我想处理每一行的数据,如下所示:
doSomething :: [Int] -> [Int] -> Int
doSomething [] y = 0 -- stop execution. I'm concerted in memory behavior only.
doSomething (x:xs) y = doSomething xs y
main = do
inputdata <- getContents
let (x:xs) = lines inputdata
firstLine = map (read ::String->Int) $ words $ x
secondLine = map (read ::String->Int) $ words $ head xs
print $ doSomething firstLine secondLine
当我运行此程序时,堆分析显示如下:
如果我不使用secondLine(xs
),那么此程序将以恒定内存运行。
列表firstLine
的每个条目都被处理,然后由GC丢弃。
为什么消耗的内存如此之大? 我看到分配的内存量大约为100MB,但实际输入数据大小为5MB。
head xs
是否强制将第一行读入内存,
甚至secondLine
都没用过?这是主要原因
堆分析图的内存增加?
如何处理具有常量内存的两行,如后一个执行图?
如果3)的答案取决于处理顺序,我该如何先处理第二行,然后再处理第一行?
答案 0 :(得分:3)
Haskell中的Q1)为什么消耗的内存如此之大?我看到分配的内存量大约为100MB,但实际输入数据大小为5MB。
String
是[Char]
的类型别名,因此沿着实际的字节,编译器还必须保留构造函数,指向盒装字符的指针和指向下一个构造函数的指针。内存中的每个字符,结果&gt;内存使用率是C风格字符串的10倍。更糟糕的是,文本会多次存储在内存中。
Q3)如何处理具有常量内存的两行,如后一执行图?
Q4)如果Q3)的答案取决于处理顺序,我该如何先处理第二行,然后再处理第一行?
不,你不能先处理第二行,因为lines
函数必须评估第一行中的每个字节才能点击&#39; \ n&#39;换行符。
Q2)head xs是否强制将整个第一行读入内存,甚至不使用secondLine?这是堆分析图的内存增加的主要原因吗?
不是head
阻止第一行被GC。如果您调整doSomething
的类型签名并直接将xs
传递给它,则仍会发生空间泄漏。重点是(非优化)编译器不知道在secondLine
最终命中第一个模式之前doSomething
未被使用,因此程序保留对{{1}的引用}。 BTW如果使用-O2编译,则程序以恒定内存运行。
导致程序空间泄漏的原因主要在于这一行:
xs
当丢弃let (x:xs) = lines inputdata
或x
时,此let子句将在转储的Core中内联。只有当它们两个都被引用以后,Core才会表现得很奇怪:它构造一个元组,通过模式匹配来破坏它,然后再将这两个部分构造成一个元组,因此通过保持对xs
的引用,该程序实际上保持不变对元组secondLine
的引用,因此第一行永远不会被GC。
核心(x, xs)
注释掉了:
secondLine
空间泄漏的核心:
Rec {
doSomething_rjH
doSomething_rjH =
\ ds_d1lv y_alG ->
case ds_d1lv of _ {
[] -> I# 0;
: x_alH xs_alI -> doSomething_rjH xs_alI y_alG
}
end Rec }
main
main =
>>=
$fMonadIO
getContents
(\ inputdata_app ->
print
$fShowInt
(doSomething_rjH
(map
(read $fReadInt)
(words
(case lines inputdata_app of _ {
[] -> case irrefutPatError "a.hs:6:9-32|(x : xs)"# of wild1_00 { };
: x_auf xs_aug -> x_auf
})))
([])))
main
main = runMainIO main
用Rec {
doSomething_rjH
doSomething_rjH =
\ ds_d1ol y_alG ->
case ds_d1ol of _ {
[] -> I# 0;
: x_alH xs_alI -> doSomething_rjH xs_alI y_alG
}
end Rec }
main
main =
>>=
$fMonadIO
getContents
(\ inputdata_app ->
-- *** Construct ***
let {
ds_d1op
ds_d1op =
case lines inputdata_app of _ {
[] -> irrefutPatError "a.hs:6:9-30|x : xs"#;
: x_awM xs_awN -> (x_awM, xs_awN)
} } in
-- *** Destruct ***
let {
xs_awN
xs_awN = case ds_d1op of _ { (x_awM, xs1_XwZ) -> xs1_XwZ } } in
let {
x_awM
x_awM = case ds_d1op of _ { (x1_XwZ, xs1_XwU) -> x1_XwZ } } in
-- *** Construct ***
let {
ds1_d1oq
ds1_d1oq = (x_awM, xs_awN) } in
print
$fShowInt
-- *** Destruct ***
(doSomething_rjH
(map
(read $fReadInt)
(words (case ds1_d1oq of _ { (x1_Xx1, xs1_Xx3) -> x1_Xx1 })))
(map
(read $fReadInt)
(words
(head (case ds1_d1oq of _ { (x1_Xx1, xs1_Xx3) -> xs1_Xx3 }))))))
main
main = runMainIO main
子句替换let
子句将修复空间泄漏:
case .. of
倾销的核心。 &#34;构造然后破坏&#34;模式这次没有发生:
doSomething :: [Int] -> [Int] -> Int
doSomething [] _ = 0 -- stop execution. I'm concerted in memory behavior only.
doSomething (_:xs) y = doSomething xs y
main :: IO ()
main = do
inputdata <- getContents
case lines inputdata of
x:xs -> do
let
firstLine = map (read ::String->Int) $ words x
secondLine = map (read ::String->Int) $ words $ head xs
print $ doSomething firstLine secondLine