Haskell Space Leak

时间:2016-01-21 08:12:54

标签: haskell memory-leaks space-leak

所有

在尝试解决一些编程测验时: https://www.hackerrank.com/challenges/missing-numbers ,我遇到了太空漏洞。

主要功能是difference,它实现了多组差异。 我发现了List':'和三重奏(,,)保持堆积 使用-hT选项分析。但是,只有大型列表是difference 两个参数,随着difference保持尾递归而收缩。 但是,随着程序的运行,列表消耗的内存不断增加。

三元组是短暂的数组结构,用于记录多个元素的每个元素的计数。但三倍所消耗的记忆力也在增加 不断增加,我找不到原因。

虽然我已经浏览过类似的空间泄漏事件。 stackoverflow中的问题, 我无法理解这个想法。当然,我有很多需要学习的东西。

我感谢任何评论。谢谢。

p.s)可执行文件是用-O2开关编译的。

$ ./difference -hT < input04.txt
Stack space overflow: current size 8388608 bytes.
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.6.3

import Data.List
import Data.Array

-- array (non-zero-count, start-offset, array_data)
array_size=101

myindex :: Int -> Int -> Int
myindex key offset
    | key >= offset = key - offset
    | otherwise     = key - offset + array_size

mylookup x (_,offset,arr) = arr ! idx
    where idx = myindex x offset

addOrReplace :: Int -> Int -> (Int, Int, Array Int (Int,Int)) -> (Int, Int, Array Int (Int,Int))
addOrReplace key value (count,offset,arr) = (count', offset, arr // [(idx,(key,value))])
    where idx = myindex key offset
          (_,prev_value) = arr ! idx
          count' = case (prev_value, value) of
                      (0,0) -> count
                      (0,_) -> count + 1
                      (_,0) -> count - 1
                      otherwise -> count

difference :: (Int,Int,Array Int (Int,Int)) -> [Int] -> [Int] -> [Int]
difference (count,offset,arr) [] []
    | count == 0 = []
    | otherwise  = [ k | x <- [0..array_size-1], let (k,v) = (arr ! x), v /= 0]
difference m (x:xs) y = difference new_m xs y
    where (_,v) = mylookup x m
          new_m = addOrReplace x (v + 1) m
difference m [] (y:ys) = difference new_m [] ys
    where (_,v) = mylookup y m
          new_m = if v == 0
            then m
            else addOrReplace y (v - 1) m

main = do
    n <- readLn :: IO Int
    pp <- getLine
    m <- readLn :: IO Int
    qq <- getLine
    let p = map (read :: String->Int) . words $ pp
        q = map (read :: String->Int) . words $ qq
        startArray = (0,head q, array (0,100) [(i,(0,0)) | i <- [0..100]] )
    putStrLn . unwords . map show . sort $ difference startArray q p

[编辑] 感谢Carl的建议,我提供了价值和数组。 我附上堆图。

[原始堆分析] [original heap] 1

[在seq&#39;值v]

之后
difference m (x:xs) y = difference new_m xs y
    where (_,v) = mylookup x m
          new_m = v `seq` addOrReplace x (v + 1) m

heap after seq'ing v

[在seq&#39;值vArray]

之后
difference m (x:xs) y = new_m `seq` difference new_m xs y
    where (_,v) = mylookup x m
          new_m = v `seq` addOrReplace x (v + 1) m

heap after seq'ing v and Array

1 个答案:

答案 0 :(得分:1)

我发现此代码有三个主要问题。

首先(而不是内存使用的原因,但绝对是性能普遍不佳的原因)Array对于这个用例来说是可怕的。当更新是O(n)时,O(1)查找是无用的。

说到,当Array循环其第一个输入时,不会强制存储在difference中的值。它们是thunks,包含指向先前版本数组中未评估查找的指针。您可以通过各种方式确保在更新阵列的同时评估该值。当difference循环其第二个输入时,它实际上是通过将值与0进行比较而意外地执行此操作。

第三,difference甚至不会强制在遍历第一个参数时评估正在创建的新数组。没有什么要求在循环的那一部分期间评估旧数组。

需要解决后两个问题以解决空间泄漏问题。第一个问题不会导致空间泄漏,只需要比所需更高的开销。