[Haskell-cafe] A first glimps on the {-# NOUPDATE #-} pragma

Joachim Breitner breitner at kit.edu
Tue Aug 28 18:44:29 CEST 2012

Dear GHC users,

I am experimenting with ways to /prevent/ sharing in Haskell, e.g. to
avoid space leaks or to speed up evaluation. A first attempt was to
duplicate closures on the heap to preserve the original one, see
http://arxiv.org/abs/1207.2017 for a detailed description and
information on the prototype implementation; no GHC patching required
for that.

Currently I am trying a different angle: Simply avoid generating the
code that will update a closure after its evaluation; hence the closure
stays a thunk and will happily re-evaluate the next time it is used.

Here is a classical example. Take this function (it is basically [f..t]
but with a fixed type and no risk of existing rules firing):

        myenum :: Int -> Int -> [Int]
        myenum f t = if f <= t
                     then f : myenum (f + 1) t
                     else []
and this example where sharing hurts performance badly:

        upd_upd n = 
            let l = myenum 0 n
            in last l + head l

The problem is that during the evaluation of "last l", the list is live
and needs to be kept in memory, although in this case, re-evaluating l
for "head l" would be cheaper. If n is 50000000, then this takes 3845ms
on my machine, measured with criterion, and a considerable amount of
memory (3000MB).

So here is what you can do now: You can mark the value as
non-updateable. We change myenum to

        myenum' :: Int -> Int -> [Int]
        myenum' f t = if f <= t then f : ({-# NOUPDATE #-} myenum' (f + 1) t) else []

and use that:

        upd_noupd n = 
            let l = myenum' 0 n
            in last l + head l
The improvement is considerable: 531ms and not much memory used (18MB)

Actually, it should suffice to put the pragma in the definition of l
without touching myenum:

        noupd_noupd n = 
            let l = {-# NOUPDATE #-} myenum 0 n
            in last l + head l

but this does not work with -O due to other optimizations in GHC. (It
does work without optimization.)

The next step would be to think of conditions under which the compiler
could automatically add the pragma, e.g. when it sees that evaluation a
thunk is very cheap but will increase memory consumption considerable.

Also this does not have to be a pragma; it could just as well be a
function "noupdate :: a -> a" that is treated specially by the compiler,
similar to the "inline" function.

If you want to play around this, feel free to fetch it from the unshare
branch of my ghc repository at http://git.nomeata.de/?p=ghc.git or
https://github.com/nomeata/ghc for the GitHub-lovers. Note that the
branch is repeatedly rebased against ghc master.


Dipl.-Math. Dipl.-Inform. Joachim Breitner
Wissenschaftlicher Mitarbeiter
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: This is a digitally signed message part
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20120828/fc92cd38/attachment.pgp>

More information about the Haskell-Cafe mailing list