[ this reply was slightly delayed because I accidentally sent it to just
  Fergus instead of the whole list... ]

Fergus Henderson <[EMAIL PROTECTED]> writes:

> The query quoted below, about heap usage in a 5-line Haskell program,
> has remained unanswered for two weeks.

Ok, I'll give it a shot.  This particular 5-line example needed about
3 cups of coffee's worth of investigation.

> On 14-Sep-1998, Martin Stein <[EMAIL PROTECTED]> wrote:
> > Hi,
> > 
> > though I think there was written enough about this theme, I can't still
> > understand the following problem (foldl' from the Prelude.hs):
> > 
> > > seq1 x = (3-x):seq1 x
> > > seq2 x = (3-x):seq2 1
> > >
> > > countOne xs = foldl' (\c x -> if x==1 then c+1 else c) 0 xs
> > >
> > > num1 n = countOne (take n (seq1 1))
> > > num2 n = countOne (take n (seq2 1))
> > 
> > num1 needs lots of heap space, num2 needs very few. Why?
> > (I checked it using ghc and '+RTS -s')

If we define foldl' as:

        foldl' f c [] = c
        foldl' f c (x:xs) = let z = f c x in seq z (foldl' f z xs)

(the version using 'strict' is similar) then the call to seq compiles
to

                let w = foldl' f z xs
                in seq z w

where w is an updatable thunk.  When the thunk for w is finally
entered, it will push an update frame and tail-call foldl'.  This
means that not only does a naive implementation of foldl' require
linear stack, it also requires linear heap for all the w thunks.
That's not good.

If the definition of seq is inlined, then we get the (much better):

                case z of z' -> foldl' f z' xs

This runs in constant stack/heap.  In GHC, seq is inlined if the -O
flag is given.

I checked this with both ghc-3.02 and ghc-4.00 with and without -O,
and except for ghc-4.00 without -O they all require a constant 200
bytes or so of heap.  The anomaly with ghc-4.00 is because the new
garbage collector doesn't implement stack squeezing (yet).  Stack
squeezing is an optimisation whereby sequences of update frames on the
stack can be squeezed into a single frame, saving the extra updates
and allowing the extra thunks in the heap to be collected.  You can
see the effect in ghc-3.02 by turning off squeezing with the -Z flag:

        ghc-3.02 test.hs
        a.out +RTS -Z -Sstderr -F2s -K4m

You might also have been tripped up by the generational collector -
with this example it seems to hang on to a lot of stuff until a major
collection, probably because something in the old generation gets
updated with a pointer into the new generation, which is retained
until a major collection is triggered.  The -F2s flag forces a 2-space
collector to be used.

Cheers,
        Simon

-- 
Simon Marlow                                             [EMAIL PROTECTED]
University of Glasgow                       http://www.dcs.gla.ac.uk/~simonm/
finger for PGP public key


Reply via email to