Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating a Big State Fast in Haskell

For my vector graphics library in Haskell I must carry around a rather big state: line stroke parameters, colors, clip path etc. I know two ways of doing this. Quoting a comment from Haskell-cafe: "I would suggest you either use a reader monad with mutable state, or a state monad with immutable state".

Here is my problem: updating a big immutable state is a performance kill. Using lots of STRefs is like writing C in Haskell: it's verbose and ugly.

Here is the immutable state:

data GfxState = GfxState {
  lineWidth :: Double,
  lineCap :: Int,
  color :: Color,
  clip :: Path,
  ...
}

setLineWidth :: Double -> State GfxState ()
setLineWidth x = modify (\state -> state { lineWidth = x })

As far as I know, "state { lineWidth = x }" creates a new GfxState and lets the old one be garbage collected. This kills performance when the state is big and updated often.

Here is the mutable state:

data GfxState s = GfxState {
  lineWidth :: STRef s Double,
  lineCap   :: STRef s Int,
  color     :: STRef s Color,
  clip      :: STRef s Path,
  ...
  many more STRefs
}

setLineWidth :: GfxState s -> Double -> ST s ()
setLineWidth state x = writeSTRef (lineWidth state) x

Now I get (GfxState s) and (ST s) and (STRef s) all over the place, which is verbose, confusing, and beats the spirit of writing short and expressive code. I could use C + FFI to read and update the big state, but since I encounter this pattern quite often, I'm hoping there's a better way.

like image 378
DJS Avatar asked Nov 24 '10 10:11

DJS


1 Answers

As an aside, you should certainly be improving your data type representation via unboxing, if you are concerned about performance:

data GfxState = GfxState {
  lineWidth :: {-# UNPACK #-}!Double,
  lineCap   :: {-# UNPACK #-}!Int,
  color     :: {-# UNPACK #-}!Color,
  clip      :: Path,
  ...
}

By unpacking the constructors, you improve the density of your data, going from a heap structure like this:

enter image description here

to the denser, stricter:

enter image description here

Now all the atomic types are laid out in consecutive memory slots. Updating this type will be much faster! BTW, 461.. is the Word representation of the pi field, a bug in my viewer library

You'll also reduce the chance of space leaks.

The cost of passing such a structure around will be very cheap, as the components will be stored in registers.

like image 95
Don Stewart Avatar answered Sep 29 '22 23:09

Don Stewart