Profiteur: a visualiser for Haskell GHC .prof files
Published on February 25, 2014 under the tag haskell
Introduction
GHC comes with some amazing tools to do profiling of Haskell programs. In
.prof
files, you can see exactly in which function most time is spent and
where most allocation is done.
However, at Erudify, we have a huge amount of Haskell code – and at this
point .prof
files can become very unwieldy, and the text representation is
harder to grok.
This is why I coded profiteur, a simple HTML-based visualiser for GHC .prof
files.
Installation
Installation is easy:
$ cabal install profiteur
Usage
Let us grab a sample program from the HaskellWiki. The code of this sample
program can be found in the appendix. I saved this file as binary-trees.hs
.
First, we compile it with profiling enabled:
$ ghc --make -auto-all -prof -rtsopts binary-trees.hs
[1 of 1] Compiling Main ( binary-trees.hs, binary-trees.o )
Linking binary-trees ...
We run it in profiling mode:
$ ./binary-trees 10 +RTS -p -RTS
stretch tree of depth 11 check: -1
2048 trees of depth 4 check: -2048
512 trees of depth 6 check: -512
128 trees of depth 8 check: -128
32 trees of depth 10 check: -32
long lived tree of depth 10 check: -1
This generates the file binary-trees.prof
. We can pass that to profiteur
:
$ profiteur binary-trees.prof
Wrote binary-trees.prof.html
Open the resulting file in your favorite (modern) browser and you are good to
go! Here is the resulting HTML file so you can have a look without installing
profiteur
.
As always, patches and pull requests are welcome on GitHub.
Appendix
Code used:
{-# LANGUAGE BangPatterns #-}
{-# OPTIONS_GHC -funbox-strict-fields #-}
--
-- The Great Computer Language Shootout
-- http://shootout.alioth.debian.org/
--
-- Contributed by Don Stewart
--
import System.Environment
import Data.Bits
import Text.Printf
data Tree = Nil | Node !Int Tree Tree
= 4
minN
!n !t = printf "%s of depth %d\t check: %d\n" s n t
io s
= do
main <- getArgs >>= readIO . head
n let maxN = max (minN + 2) n
= maxN + 1
stretchN
-- stretch memory tree
let c = check (make 0 stretchN)
"stretch tree" stretchN c
io
-- allocate a long lived tree
let long = make 0 maxN
-- allocate, walk, and deallocate many bottom-up binary trees
let vs = depth minN maxN
mapM_ (\((m,d,i)) -> io (show m ++ "\t trees") d i) vs
-- confirm the the long-lived binary tree still exists
"long lived tree" maxN (check long)
io
-- generate many trees
depth :: Int -> Int -> [(Int,Int,Int)]
!d !m
depth | d <= m = (2*n,d,sumT d n 0) : depth (d+2) m
| otherwise = []
where !n = 1 `shiftL` (m - d + minN)
-- allocate and check lots of trees
sumT :: Int -> Int -> Int -> Int
!d 0 t = t
sumT = sumT d (i-1) (t + a + b)
sumT d i t where a = check (make i d)
= check (make (-i) d)
b
-- traverse the tree, counting up the nodes
check :: Tree -> Int
Nil = 0
check Node i l r) = i + check l - check r
check (
-- build a tree
make :: Int -> Int -> Tree
0 = Node i Nil Nil
make i = Node i (make (i2-1) d2) (make i2 d2)
make i d where i2 = 2*i; d2 = d-1