[Haskell-cafe] GHC predictability
gale at sefer.org
Mon May 12 19:27:05 EDT 2008
Andrew Coppin wrote:
> I offer up the following example:
> mean xs = sum xs / length xs
> Now try, say, "mean [1.. 1e9]", and watch GHC eat several GB of RAM. (!!)
> If we now rearrange this to
> mean = (\(s,n) -> s / n) . foldr (\x (s,n) -> let s' = s+x; n' = n+1 in s'
> `seq` n' `seq` (s', n')) (0,0)
> and run the same example, and watch it run in constant space.
> Of course, the first version is clearly readable, while the second one is
> almost utterly incomprehensible, especially to a beginner. (It's even more
> fun that you need all those seq calls in there to make it work properly.)
You can write it like this:
mean = uncurry (/) . foldl' (\(s,n) x -> ((,) $! s+x) $! n+1) (0,0)
I don't think that's so bad. And for real-life examples, you almost
never need the ($!)'s or seq's - your function will do some kind
of pattern matching that will force the arguments. So really, all
you need to remember is: if you're repeating a fast calculation across
a big list, use foldl'. And insertWith', if you're storing the result in
a Data.Map. That's about it.
> The sad fact is that if you just write something in Haskell in a nice,
> declarative style, then roughly 20% of the time you get good performance,
> and 80% of the time you get laughably poor performance.
I don't know why you think that. I've written a wide variety of functions
over the past few years. I find that when performance isn't good enough,
it's because of the algorithm, not because of laziness. Laziness
works for me, not against me.
Of course, it depends what you mean by "good performance". I have
never needed shootout-like performance. But to get that, you need
some messy optimization in any language.
More information about the Haskell-Cafe