[Template-haskell] On reifing functions and partial evaluation

Duncan Coutts duncan at coutts.uklinux.net
Tue Feb 17 02:49:54 EST 2004


Ok, here's something pointless but fun: improving ghc's Ackerman
function micro-benchmark using Template Haskell and partial evaluation.

First the sensational headline:
ghc beats gcc by 57% on Ackerman benchmark, improved to 290% using
Template Haskell.

In my previous post I looked at a the generating extensions of a couple
simple non-recursive functions. The generating extension for the
Ackerman function is rather more complicated:

ack :: Int -> Int -> Int
ack m n =
  if m == 0 then n+1
            else if n == 0 then ack (m-1) 1
                           else ack (m-1) (ack m (n-1))

genex_ack :: Int -> ExpQ
genex_ack m = 
  if m == 0 then [| \n -> n + 1 |]
            else [| let genex_ack_m = \n -> 
                          if n == 0 then $(genex_ack (m-1)) 1
                                    else $(genex_ack (m-1)) (genex_ack_m (n-1))
                     in genex_ack_m |]

In particular it is complicated by the fact that genex_ack m calls
itself without decreasing m (for m > 0). We can't use $(genex_ack m) or
it would not terminate, hence memoising genex_ack m to use in the
recursive call. This could probably be done more elegantly/regularly.

So then we use it in our benchmark like so:

ack_4       = ack 4
genex_ack_4 = $(genex_ack 4)

and time it (in ghci, having compiled with ghc -O):

ack_4 1		: approx 100 sec
genex_ack_4 1	: approx 40  sec

for comparison with gcc-3.3.2 -O3 -fomit-frame-pointer (source taken
from http://www.bagley.org/~doug/shootout/bench/ackermann/)

(gcc) ack 4 1	: approx 157 sec


So there you have it, we can cheat with the best of them to improve our
scores on pointless micro-benchmarks. :-)

Duncan



More information about the template-haskell mailing list