[Haskell-beginners] How to hunt this space-leak
martin
martin.drautzburg at web.de
Sun Jun 12 13:38:02 UTC 2016
Hello all,
I hope I am not asking for too much, as to explain my problem, you need to read my code, which may not be a pleasure.
I am trying to write a "Logger", which formats and accumulates log-entries in a monoid. From there I went to writing an
"accumulating" logger, i.e. one which can accumulate (sum/avg) over entries made with a certain timespan.
My problen is memory consumption. In the test below I stress the logger with 1 million values, but in the end result,
there are only 10 entries left, because it accumulates over 100,000. Memory goes up to apx 100MB.
When I comment out the line, which logs the accumulated value (see -- > below), memory stays below 10MB. I dont
understand why this is so. After all, the difference is only whether or not those 10 entries are logged or not.
Can someone explain this?
{-# LANGUAGE BangPatterns#-}
import Data.Monoid
import Control.Monad.State.Strict
import System.TimeIt
import Text.Show.Pretty
import Debug.Trace
------------------------------------------------------------
-- Simple Time Stuff
------------------------------------------------------------
type Instant = Double
type Interval = Double
type Timed a = (Instant, a)
instant = fst
------------------------------------------------------------
-- Logger based on State monad
------------------------------------------------------------
data SLogger a l = SLgr {runSLogger :: a ->State l (SLogger a l)}
type SLogFormatter a l = a -> l
accLogger :: (Monoid c, Show a) =>
(Instant, Interval, [Timed b]) ->
SLogFormatter (Timed a) [Timed b] -> SLogFormatter [Timed b] [Timed c] -> SLogger (Timed a) [Timed c]
accLogger (tx, dt, tas) fmt1 fmt2 = SLgr $ \(!ta) ->
let x = fmt1 ta
!tas' = x <> tas
in
if instant ta < tx
then do
-- keep accumulating
return $ accLogger (tx, dt, tas') fmt1 fmt2
else do
-- compute new log and reset accumulator
!l0 <- get
-- > put $ fmt2 tas' <> l0
return $ accLogger ((tx+dt), dt, []) fmt1 fmt2
accFmt1 ta = [ta]
accFmt2 tas = [(fst $ head tas, "hello from accFormatter")]
-- apply logger to a list of as
stest lgr [] = return lgr
stest lgr (a:as) = do
lgr' <- (runSLogger lgr) a
stest lgr' as
main2 = do
let as = zip [1.0 .. 1000000.0] [1..1000000] :: [(Instant, Int)]
log = execState (stest (accLogger (100000.0,100000.0,[]) accFmt1 accFmt2 ) as) [(0,"init")]
timeIt $ putStrLn $ ppShow log
putStrLn "done"
main = main2
More information about the Beginners
mailing list