Suresh Sudhakaran
Suresh Sudhakaran

Reputation: 41

GHC profiler output

I have been going through a blog discussing space leaks in Haskell and have been trying to understand the graph output provided by the ghc profiler (after using hp2ps)

Specifically this is the code that I am looking at:

main = print (f [1..4000000] (0 :: Int, 1 :: Int))

f [] c = c   
f (x:xs) c = f xs (tick x c) 

tick x (c0,c1) | even x = (c0,c1+1)
               | otherwise = (c0+1,c1)

I ran the program with -hb flag for biographical profiling of the heap:

Biographical profile of heap

I can't understand why so much memory is being considered in the void category as it means that a lot of memory is being allocated to objects that are never used. I restricted a producer profile to just the void components getting output for producer profile restricted to void component:

hc output restricted to void

Is there some way to find out what exactly what thunks are being formed that lead do the void usage?

Upvotes: 4

Views: 198

Answers (1)

chi
chi

Reputation: 116139

Use more (local) variables. Replace

main = print (f [1..4000000] (0 :: Int, 1 :: Int))

with

main = print (f list (0 :: Int, 1 :: Int))
   where
   list = [1..4000000]

In such way, you will see an entry main.list/main/Main.CAF showing exactly how much space the 4M-long list takes. You'll probably find out that it takes a lot of space, since it is being defaulted to [Integer]. Define it as [Int] to improve space.

Further, f/main/... is already shown to take a lot of space in your own post. Indeed, it is similar to a left non-strict fold, which has bad performance.

Making things stricter, we get

f [] c = c   
f (x:xs) c = f xs $! tick x c 

tick x (c0,c1) | even x = (,) c0 $! c1+1
               | otherwise = ((,) $! c0+1) c1

And now the code runs in constant time, even without using Int.

GHC 8.0.1 would also do this optimization automatically if we turn on optimization with -O2.

Generally, though, it's a good idea to use explicit type annotations, so that GHC does not introduce unwanted/unneeded polymorphism, and performs better type checks. This could also trigger more optimizations, in general.

Upvotes: 1

Related Questions