Space 动态泄漏 Haskell
Space leak in dynamic Haskell
我几天前发布了这个问题: 并且被推荐使用 ByteStrings 而不是 Strings。使用 ByteStrings 实现算法后,程序崩溃,超出内存限制。
import Control.Monad
import Data.Array.IArray
import qualified Data.ByteString as B
main = do
n <- readLn
pairs <- replicateM n $ do
s1 <- B.getLine
s2 <- B.getLine
return (s1,s2)
mapM_ (print . editDistance) pairs
editDistance :: (B.ByteString, B.ByteString) -> Int
editDistance (s1, s2) = dynamic editDistance' (B.length s1, B.length s2)
where
editDistance' table (i,j)
| min i j == 0 = max i j
| otherwise = min' (table!((i-1),j) + 1) (table!(i,(j-1)) + 1) (table!((i-1),(j-1)) + cost)
where
cost = if B.index s1 (i-1) == B.index s2 (j-1) then 0 else 1
min' a b = min (min a b)
dynamic :: (Array (Int,Int) Int -> (Int,Int) -> Int) -> (Int,Int) -> Int
dynamic compute (xBnd, yBnd) = table!(xBnd,yBnd)
where
table = newTable $ map (\coord -> (coord, compute table coord)) [(x,y) | x<-[0..xBnd], y<-[0..yBnd]]
newTable xs = array ((0,0),fst (last xs)) xs
内存消耗似乎与 n
成比例。输入字符串的长度为 1000 个字符。我希望 Haskell 在打印每个解决方案后释放 editDistance
中使用的所有内存。不是这样吗?如果没有,我该如何强制执行此操作?
我看到的唯一其他真正的计算是针对 cost
但用 seq
强制计算没有任何效果。
我的感觉是问题是你的min'
不够严格。因为它不强制其参数,所以它只是为每个数组元素构建一个 thunk。这会导致使用更多内存、GC 次数增加等。
我会尝试:
{-# LANGUAGE BangPatterns #-}
...
min' !a !b !c = min a (min b c)
当然,如果您在计算任何结果和打印输出之前读取所有 n
个输入,您的内存会随着 n
增加。您可以尝试交错输入和输出操作:
main = do
n <- readLn
replicateM_ n $ do
s1 <- B.getLine
s2 <- B.getLine
print (editDistance (s1,s2))
或者使用惰性 IO(未经测试,可能需要无偿 B.
):
main = do
n <- readLn
cont <- getContents
let lns = take n (lines cont)
pairs = unfoldr (\case (x:y:rs) -> Just ((x,y),rs) ; _ -> Nothing) lns
mapM_ (print . editDistance) pairs
编辑:其他可能的节省包括使用未装箱的数组,并且在数组构造期间不通过 last
强制整个 strLen^2
大小列表。考虑 array ((0,0),(xBnd,yBnd)) xs
.
我几天前发布了这个问题:
import Control.Monad
import Data.Array.IArray
import qualified Data.ByteString as B
main = do
n <- readLn
pairs <- replicateM n $ do
s1 <- B.getLine
s2 <- B.getLine
return (s1,s2)
mapM_ (print . editDistance) pairs
editDistance :: (B.ByteString, B.ByteString) -> Int
editDistance (s1, s2) = dynamic editDistance' (B.length s1, B.length s2)
where
editDistance' table (i,j)
| min i j == 0 = max i j
| otherwise = min' (table!((i-1),j) + 1) (table!(i,(j-1)) + 1) (table!((i-1),(j-1)) + cost)
where
cost = if B.index s1 (i-1) == B.index s2 (j-1) then 0 else 1
min' a b = min (min a b)
dynamic :: (Array (Int,Int) Int -> (Int,Int) -> Int) -> (Int,Int) -> Int
dynamic compute (xBnd, yBnd) = table!(xBnd,yBnd)
where
table = newTable $ map (\coord -> (coord, compute table coord)) [(x,y) | x<-[0..xBnd], y<-[0..yBnd]]
newTable xs = array ((0,0),fst (last xs)) xs
内存消耗似乎与 n
成比例。输入字符串的长度为 1000 个字符。我希望 Haskell 在打印每个解决方案后释放 editDistance
中使用的所有内存。不是这样吗?如果没有,我该如何强制执行此操作?
我看到的唯一其他真正的计算是针对 cost
但用 seq
强制计算没有任何效果。
我的感觉是问题是你的min'
不够严格。因为它不强制其参数,所以它只是为每个数组元素构建一个 thunk。这会导致使用更多内存、GC 次数增加等。
我会尝试:
{-# LANGUAGE BangPatterns #-}
...
min' !a !b !c = min a (min b c)
当然,如果您在计算任何结果和打印输出之前读取所有 n
个输入,您的内存会随着 n
增加。您可以尝试交错输入和输出操作:
main = do
n <- readLn
replicateM_ n $ do
s1 <- B.getLine
s2 <- B.getLine
print (editDistance (s1,s2))
或者使用惰性 IO(未经测试,可能需要无偿 B.
):
main = do
n <- readLn
cont <- getContents
let lns = take n (lines cont)
pairs = unfoldr (\case (x:y:rs) -> Just ((x,y),rs) ; _ -> Nothing) lns
mapM_ (print . editDistance) pairs
编辑:其他可能的节省包括使用未装箱的数组,并且在数组构造期间不通过 last
强制整个 strLen^2
大小列表。考虑 array ((0,0),(xBnd,yBnd)) xs
.