内存使用异常(内存泄漏?



我有以下类型和两个相关函数,我打算将它们作为大型列表折叠的一部分进行测量:

类型和访问功能:

data Aggregate a = Aggregate (Maybe a) (a -> Aggregate a)
get :: Aggregate a -> Maybe a
get (Aggregate get' _) = get'
put :: Aggregate a -> a -> Aggregate a
put (Aggregate _ put') = put'

第一个功能:

updateFirst :: Maybe a -> a -> Aggregate a
updateFirst cur val = Aggregate new (updateFirst new)
where new = mplus cur (Just val)
first :: Aggregate a
first = Aggregate Nothing (updateFirst Nothing)

第二个功能:

updateMinimum :: Ord a => Maybe a -> a -> Aggregate a
updateMinimum cur val = Aggregate new (updateMinimum new)
where new = min <$> (mplus cur (Just val)) <*> Just val
minimum :: Ord a => Aggregate a
minimum = Aggregate Nothing (updateMinimum Nothing)

这些函数的编写方式是内存应该是常量的。因此,我选择在 GHC 中使用Strict语言扩展,这迫使对 thunks 进行评估。Weigh库用于执行分配测量:

test :: A.Aggregate Double -> Int -> Maybe Double
test agg len = A.get $ F.foldl' A.put agg (take len $ iterate (+0.3) 2.0)
testGroup :: String -> A.Aggregate Double -> Weigh ()
testGroup name agg = sequence_ $ map (cnt -> func (str cnt) (test agg) cnt) counts
where
counts  = map (10^) [0 .. 6]
str cnt = name ++ (show cnt)
main :: IO ()
main =
mainWith
(do setColumns [Case, Allocated, Max, GCs]
testGroup "fst" A.first
testGroup "min" A.minimum
)

Weigh输出如下:

Case          Allocated          Max  GCs
fst1                304           96    0
fst10             2,248           96    0
fst100           21,688           96    0
fst1000         216,088           96    0
fst10000      2,160,088           96    2
fst100000    21,600,088           96   20
fst1000000  216,000,088           96  207
min1                552           96    0
min10             4,728           96    0
min100           46,488           96    0
min1000         464,088           96    0
min10000      4,967,768           96    4
min100000    49,709,656    6,537,712   44
min1000000  497,226,840  103,345,512  445

为什么 GHC 突然在大小为 10^5 和 10^6 的输入中分配了更多的内存?我的 GHC 版本是8.4.4.

Haskell中的严格性注释可以说是"关系性的"。他们说,每当评估其他东西到 WHNF 时,都必须将某些东西评估为 WHNF(弱头范式(。

对于函数参数,这意味着在函数应用程序本身计算为 WHNF 之前,函数参数将被计算为 WHNF。

对于严格字段,这意味着每当将包含值计算为 WHNF 时,该字段都将被评估为 WHNF。这对于维护用作累加器的数据类型中的"严格链"很有用(例如,用作foldl'累加器的数据类型(。否则,即使包含值保留在 WHNF 中,thunks 也可以隐藏在惰性字段后面,并导致空间泄漏。特别是,元组没有严格的组件,并且是累加器中空间泄漏的常见来源。

Maybe也不严格要求Just构造函数中包含的值。事实上,这就是问题的原因。Just内的价值在foldl'过程中从未被强迫过,并且在那里积累了投篮。

为什么Strict不防止问题?因为它只影响当前模块中的函数和数据类型定义,而Maybe是在其他地方定义的。解决方案是在每次迭代时手动强制Just中的值,或者定义您自己的"严格Maybe"。

相关内容

  • 没有找到相关文章

最新更新