如何使用常量内存处理两个长行?

时间:2016-01-22 07:23:25

标签: performance haskell lazy-evaluation

输入文件由两行组成,每行包含多个数字

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丢弃。

  1. 为什么消耗的内存如此之大? 我看到分配的内存量大约为100MB,但实际输入数据大小为5MB。

  2. head xs是否强制将第一行读入内存, 甚至secondLine都没用过?这是主要原因 堆分析图的内存增加?

  3. 如何处理具有常量内存的两行,如后一个执行图?

  4. 如果3)的答案取决于处理顺序,我该如何先处理第二行,然后再处理第一行?

1 个答案:

答案 0 :(得分:3)

  

Q1)为什么消耗的内存如此之大?我看到分配的内存量大约为100MB,但实际输入数据大小为5MB。

Haskell中的

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