Profiling GHC

As years pass and as the Haskell language gets more and more complicated, comes the need to profile the performance of the Haskell compiler itself. With GHC, profiling a Haskell program is not that hard, but combined with a non-trivial build process of GHC itself, it can lead to some issues.

Building GHC

In GHC, profiling has a non-zero overhead in the RTS. Therefore whenever you want to be able to profile a program, you need to say so at compile time, and your program will be linked with a special p version of the RTS which allows for profiling. GHC itself is no different: you need to compile GHC with profiling enabled.

Fortunately, someone had figured this out for us before, and GHC has a mk/flavours/ flavor, which builds a p RTS, and links stage2 GHC with that. Unfortunately it doesn't seem that profiling is well-maintained. If you want to also enable GhcDebugged = YES you'll find out that the debug_p RTSes are never built. I had to manually adjust that in mk/ in the GhcRTSWays variable. In general the finite lattice of RTS ways is not easily expressible in make language.

From then on compilation is fairly straightforward. ./boot, ./configure make. Now the binary in inplace/bin/ghc-stage2 (or wherever it got installed if you chose to install) can be profiled as usual with +RTS -p.

Looking Deeper into It

GHC by default is built with -fprof-auto with a few manually placed SCC annotations in key places. As the comment in compiler/ suggests, otherwise the profile would be unreadable, and if you wish to look into the composition of a specific cost centre you can enable more agressive profile annotation options for a specific module.

Bonus Points: Bootstrapping Cabal

Sometimes the code on which we profile GHC might depend on other packages, and this is where cabal steps in. After being unable to make my distribution's cabal work with the GHC that I had just compiled, I discovered a nice tool: libraries/Cabal/cabal-install/ The easiest way to go about it is to ./configure GHC with a --prefix=~/.local or somewhere similar, and install it there, and then run: PREFIX=~/.local/ GHC=~/.local/bin/ghc ./ --global

It should be noted, though, that the versions of a few dozen packages are hardcoded into the script, and they might not always be compatible with the wired-in libraries (base/ghc/ghc-prim) that you've just built. Expect to have to change these in the script (look for e.g. TEXT_VER) and libraries/Cabal/cabal-install/cabal-install.cabal. This also lets us build a profiled cabal.

Post comment
Use [code][/code] and [code=python][/code] for formatting.