[Haskell-beginners] Space leak debugging
Daniel Fischer
daniel.is.fischer at web.de
Thu Jun 3 17:51:40 EDT 2010
On Thursday 03 June 2010 22:18:12, Philip Scott wrote:
> Hello again, it's me, your favourite haskell beginner :)
>
> This one really has me puzzled. I've got a little example here - the
> program itself is a bit silly but it illustrates my problem nicely.
>
> He's a little program:
>
> import qualified Data.ByteString as B
>
> chunkFiller i bs = do
> if (B.null bs ) then return 1
> else chunkFiller (i+1) bs
>
> main = do
> let bs = (B.singleton 10)
> j <- chunkFiller 0 bs
> return 1
>
> It runs forever, which is fine, but it doesn't run in constant space. In
> fact, it gobbles up all the memory until it can't get any more and dies.
> I can't for the life of me work out what is going on, surely there is
> some enormous thunk being built up somewhere,
Yep.
chunkFiller 0 bs
~> chunkFiller (0+1) bs
~> chunkFiller ((0+1)+1) bs
~> chunkFiller (((0+1)+1)+1) bs
~> ...
Since the loop doesn't do much, the thunk grows really fast:
$ ./philLeak +RTS -s -M400M
./philLeak +RTS -s -M400M
Heap exhausted;
Current maximum heap size is 419430400 bytes (400 MB);
use `+RTS -M<size>' to increase it.
414,708,668 bytes allocated in the heap
1,173,761,740 bytes copied during GC
414,303,860 bytes maximum residency (11 sample(s))
5,643,660 bytes maximum slop
418 MB total memory in use (3 MB lost due to fragmentation)
Generation 0: 781 collections, 0 parallel, 2.06s, 2.16s elapsed
Generation 1: 11 collections, 0 parallel, 7.64s, 13.11s elapsed
INIT time 0.00s ( 0.00s elapsed)
MUT time 0.26s ( 0.26s elapsed)
GC time 9.70s ( 15.27s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 9.95s ( 15.54s elapsed)
%GC time 97.4% (98.3% elapsed)
Alloc rate 1,619,867,147 bytes per MUT second
Productivity 2.6% of total user, 1.6% of total elapsed
> but for all the
> Debug.Trace(), $!, seq's, and profilign outputs in the world I can't
> figure out where.
a) BangPatterns, put a bang on the i,
chunkFiller !i bs = ...
b) else i `seq` chunkFiller (i+1) bs
c) else (chunkFiller $! i+1) bs
all of those cure the space leak.
> What is *really* interesting is that if you replace
> the 'if' line with
>
> if (B.null bs || i== -1) then return 1
>
> (i never equals -1) then it runs in constant space! That just boggles my
> mind.
bs isn't null, so it must check whether i == (-1). For that, it must
evaluate i, hence it doesn't build a thunk.
>
> As usual, any thoughts greatly appreciated.
>
> All the best,
>
> Philip
More information about the Beginners
mailing list