У меня есть следующий тип и две соответствующие функции, которые я намереваюсь измерить как часть большого списка:
Тип и функции доступа:
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)
Функции написаны таким образом, что память должна быть постоянной. Поэтому я решил использовать языковое расширение Strict
в GHC, которое принудительно оценивает переходники. Библиотека 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
.
new = ...
, который не оценивается до тех пор, пока не понадобится из-за лени. Попробуйте сделать это перед возвратом результата, как вupdateFirst cur val = new `seq` Aggregate new (updateFirst new)
. В качестве альтернативы также может подойти рисунок челки. (Может быть, нужно больше форсирования, но я бы начал с этого.) - person chi   schedule 29.12.2019updateFirst
, использует расширениеStrict
— насколько я понимаю, он автоматически форсирует все переходники — не так ли? - person Daniel Lovasko   schedule 29.12.2019Aggregate
. Тем не менее, я бы попробовал его протестировать. - person chi   schedule 29.12.2019Strict
скромнее этого. Это эквивалентно тому, чтобы сделать поля всех типов данных, определенных в модуле, строгими. И строгость поля просто означает, что всякий раз, когда значение типа данных оценивается как WHNF — скажем, как аккумуляторfoldl'
— поле также будет оцениваться как WHNF. Обратите внимание, что, в частности,Maybe
не становится строгим, как это определено в другом месте. Возможно, это часть проблемы. Попробуйте указать значение внутриJust
, а не только во внешнемMaybe
. - person danidiaz   schedule 29.12.2019Strict
, я думаю, что вы описываетеStrictData
, который делает типы строгими, но чистое расширениеStrict
также должно достигать аргументов функции. - person Daniel Lovasko   schedule 29.12.2019Just
с помощьюBangPatterns
действительно было ключом к устранению утечки памяти. Спасибо! Не могли бы вы опубликовать свой комментарий в качестве ответа, чтобы я мог принять его, пожалуйста? - person Daniel Lovasko   schedule 29.12.2019