Running out of memory in a simple monad
David Bergman
davidb@home.se
Mon, 16 Dec 2002 09:34:47 +0100
You are right,
After writing that e-mail I looked at a lot of cases in Hugs, and also
encountered this CAF problem. And, as I pointed out elsewhere, the "last
call optimisation" is not very interesting in the lazy evaluation
scenario...
One problem, though, is that I would like not to get rid of the CAF,
since I (presumably wrongly) assume that CAFs are implemented more
efficiently in Hugs than "normal" definitions. Am I right in this
assumption?
Thanks,
David
> -----Original Message-----
> From: haskell-admin@haskell.org
> [mailto:haskell-admin@haskell.org] On Behalf Of Alastair Reid
> Sent: Monday, December 16, 2002 9:18 AM
> To: David Bergman
> Cc: 'Richard Uhtenwoldt'; haskell@haskell.org
> Subject: Re: Running out of memory in a simple monad
>
>
>
> "David Bergman" <davidb@home.se> writes:
> > Note: In an unoptimized scenario, such as
> > with Hugs, you do indeed run out of memory in your "loop"
> (after some
> > 40000 iterations) not having the recursion in the last call. Even
> > loops not constructing cons cells do, such as
>
> > loop 0 = return ()
> > loop n = do { print n; loop (n-1) } -- print n >> loop (n-1)
> > main = loop 50000
>
> > (you see Hugs die after some 25000 iterations...)
>
> I'm afraid your diagnosis is way off base here.
>
> The problem is nothing to do with a 'last call optimization'
> or with the do syntactic sugar and can be worked around not
> by changing how you _write_your code (which would obviously
> be a large burden) but by how you _call_ your code (a much
> smaller burden).
>
> The problem is to do with garbage collection of Constant
> Applicable Forms (or CAFs). CAFs are definitions like:
>
> nil = []
> one = 1 :: Int
> primes = ...
> main = loop 50000
>
> GHC and Hugs differ in how they treat CAFs. Hugs treats CAFs
> as a special case and never garbage collects a CAF - the only
> way to discard its value is to unload the module that defines
> it. GHC treats CAFs the same as normal definitions and
> garbage collects them when they can no longer contribute to
> future execution.
>
> The difference between these behaviours can be seen when the
> CAF grows very large as in your example.
>
> The workaround is simple enough: add a dummy argument to the
> CAF (so that it is not a CAF any more):
>
> main _ = loop 50000
>
> and then specify the extra argument when invoking it:
>
> main ()
>
> (This is a pretty standard optimisation technique: we're
> trading time to recompute a result for the space taken to
> store the result. Coming from other languages where actions
> (i.e., monadic computations) are not first class values, this
> is a bit surprising but, from a Haskell perspective, it is
> completely uniform.)
>
>
> Note that this workaround is necessary with GHC too if you
> have a large CAF which does not die. For example, if you
> wanted to benchmark your code, you might want to run it 10
> times using:
>
> > main1 = loop 50000
> > main = sequence_ (replicate 10 main1)
>
> Now main1 will not be garbage collected until the last time
> it is executed. The solution is the same as for Hugs: add a
> dummy argument.
>
> --
> Alastair Reid alastair@reid-consulting-uk.ltd.uk
> Reid Consulting (UK) Limited
> http://www.reid-consulting-uk.ltd.uk/alastair/
>
> _______________________________________________
> Haskell mailing list
> Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
>