Reputation: 41
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:
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:
Is there some way to find out what exactly what thunks are being formed that lead do the void usage?
Upvotes: 4
Views: 198
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